Подтвердить что ты не робот

Исправлено арифметическое исключение во время BigDecimal.divide

Я думал, что java.math.BigDecimal должен быть "Ответ & торговля"; к необходимости выполнения бесконечной точности арифметики с десятичными числами.

Рассмотрим следующий фрагмент:

import java.math.BigDecimal;
//...

final BigDecimal one = BigDecimal.ONE;
final BigDecimal three = BigDecimal.valueOf(3);
final BigDecimal third = one.divide(three);

assert third.multiply(three).equals(one); // this should pass, right?

Я ожидаю, что assert пройдет, но на самом деле выполнение даже не доходит: one.divide(three) вызывает выброс ArithmeticException!

Exception in thread "main" java.lang.ArithmeticException:
Non-terminating decimal expansion; no exact representable decimal result.
    at java.math.BigDecimal.divide

Оказывается, это поведение явно задокументировано в API:

В случае divide точное частное может иметь бесконечно длинное десятичное разложение; например, 1 делится на 3. Если фактор имеет неограничивающее десятичное расширение, и операция указана для возврата точного результата, генерируется ArithmeticException. В противном случае возвращается точный результат деления, как это сделано для других операций.

Далее, просматривая API, обнаруживается, что на самом деле существуют различные перегрузки divide, которые выполняют неточное деление, то есть:

final BigDecimal third = one.divide(three, 33, RoundingMode.DOWN);
System.out.println(three.multiply(third));
// prints "0.999999999999999999999999999999999"

Конечно, теперь очевидный вопрос: "Какая точка???". Я думал, что BigDecimal является решением, когда нам нужна точная арифметика, например. для финансовых расчетов. Если мы точно не можем даже divide, то насколько это полезно? Действительно ли это служит общей цели, или это полезно только в очень нишевом приложении, где вам, к счастью, просто не нужно divide вообще?

Если это не правильный ответ, что мы можем использовать для точного деления в финансовых расчетах? (Я имею в виду, у меня нет финансового специалиста, но они все еще используют деление, правильно?).

4b9b3361

Ответ 1

Если это не правильный ответ, что мы можем использовать для точного деления в финансовых расчетах? (Я имею в виду, у меня нет финансового специалиста, но они все еще используют деление, правильно?).

Ты правильно учился в средней школе? Вы узнали о том, как (например), когда вы делитесь на 1 на 3, вы получаете 0.33333... т.е. Повторяющееся десятичное число? Разделение чисел, представленных в десятичной форме, НЕ является точным.

Когда вы выполняете финансовые расчеты с разделением, вы должны учитывать, что делать с повторяющейся долей. Вы можете округлить его или вниз, или до ближайшего целого числа, или что-то еще, но в основном вы не можете просто забыть о проблеме.

BigDecimal javadoc говорит следующее:

Класс BigDecimal позволяет пользователю полностью контролировать поведение округления. Если не указан режим округления и точный результат не может быть представлен, генерируется исключение; в противном случае расчеты могут быть выполнены в выбранном режиме точности и округления путем подачи соответствующего объекта MathContext к операции.

Другими словами, вы должны сказать BigDecimal, что делать с округлением.

EDIT - в ответ на эти действия из OP.

Как BigDecimal обнаруживает бесконечное повторяющееся десятичное число?

Он явно не обнаруживает повторяющееся десятичное число. Он просто обнаруживает, что результат некоторой операции не может быть представлен точно с использованием указанной точности; например слишком много цифр требуется после десятичной точки для точного представления.

Он должен отслеживать и обнаруживать цикл в дивиденде. Он МОЖЕТ БЫТЬ ИСПОЛЬЗОВАТЬ этот другой способ, обозначая, где находится повторяющаяся часть, и т.д.

Я полагаю, что BigDecimal можно было бы указать, чтобы точно представлять повторяющееся десятичное число. (Или, говоря иначе, это будет эффективно BigRational.) Однако это сделало бы реализацию намного сложнее, это сделало бы все операции более дорогими и сделало бы невозможным, чтобы приложения помещали привязку в пространство используемые объектами BigDecimal в результате сложных вычислений; например степенных рядов, которые аппроксимируют иррациональность.

Суть в том, что эта дополнительная сложность и стоимость исполнения не подходят для типичных случаев использования BigDecimal. Сюда входят финансовые расчеты, в которых соглашения о бухгалтерском учете не позволяют использовать повторяющиеся десятичные знаки.

Ответ 2

Класс BigDecimal not BigFractional. Из некоторых ваших комментариев это звучит так, как будто вы просто жалуетесь, что кто-то не строил все возможные алгоритмы обработки номера в этом классе. Финансовые приложения не нуждаются в бесконечной десятичной точности; просто абсолютно точные значения требуемой точности (обычно 0, 2, 4 или 5 десятичных цифр).

На самом деле я имел дело со многими финансовыми приложениями, использующими double. Мне это не нравится, но так было написано (не на Java). Когда есть обменные курсы и конверсии единиц, тогда есть и потенциал проблем округления и синяков. BigDecimal устраняет позднее, но для деления все еще существует.

Ответ 3

Если вы хотите работать с десятичными знаками, а не с рациональными числами, и вам нужна точная арифметика перед окончательным округлением (округление до центов или что-то еще), вот небольшой трюк.

Вы всегда можете манипулировать своими формулами, чтобы было только одно окончательное разделение. Таким образом, вы не потеряете точность во время вычислений, и вы всегда получите правильно округленный результат. Например,

a/b + c

равно

(a + bc) / b.

Ответ 4

Кстати, я бы очень признателен понимание людей, которые работали с финансового программного обеспечения. Я часто слышал BigDecimal выступает за двойную

В финансовых отчетах мы используем alwasy BigDecimal со шкалой = 2 и ROUND_HALF_UP, поскольку все напечатанные значения в отчете должны быть приведены к воспроизводимый результат. Если кто-то проверяет это, используя простой калькулятор.

В Швейцарии они округляются до 0,05, так как у них больше нет 1 или 2 Rappen монеты.

Ответ 5

Есть ли необходимость в

a=1/3;
b=a*3;

resulting in

b==1;

в финансовых системах? Наверное, нет. В финансовых системах определяется, какой раунд и масштаб необходимо использовать при выполнении расчетов. В некоторых ситуациях круглый цикл и масштаб определяются в законе. Все компоненты могут полагаться на такое определенное поведение. Возврат b == 1 будет неудачным, поскольку он не будет соответствовать указанному поведению. Это очень важно при расчете цен и т.д.

Это похоже на спецификации IEEE 754 для представления поплавков в двоичных цифрах. Компонент не должен оптимизировать "лучшее" представление без потери информации, поскольку это нарушит контракт.

Ответ 6

Вы должны предпочесть BigDecimal для финансовых расчетов. Округление должно быть указано бизнесом. Например. сумма (100,00 $) должна быть разделена поровну на три счета. Должно быть бизнес-правило, в котором учетная запись принимает дополнительный процент.

Double, float не используются для использования в финансовых приложениях, потому что они не могут представлять доли 1 точно, которые не являются показателями 2. Например. рассмотрим 0,6 = 6/10 = 1 * 1/2 + 0 * 1/4 + 0 * 1/8 + 1 * 1/16 +... = 0.1001... b

Для математических вычислений вы можете использовать символическое число, например. сохраняя знаменатель и числитель или даже целое выражение (например, это число составляет sqrt (5) +3/4). Поскольку это не основной вариант использования java api, вы выиграли его там.

Ответ 7

Чтобы разделить сохранение, вы должны установить MATHcontext,

BigDecimal bd = new BigDecimal(12.12, MathContext.DECIMAL32).divide(new BigDecimal(2)).setScale(2, RoundingMode.HALF_UP);

Ответ 8

Я согласен с тем, что Java не имеет большой поддержки для представления фракций, но вы должны понимать, что при работе с компьютерами невозможно полностью контролировать ситуацию. По крайней мере, в этом случае исключение сообщает вам, что точность теряется.

Насколько я знаю, "бесконечная точность арифметики с десятичными числами" просто не произойдет. Если вам нужно работать с десятичными знаками, то, что вы делаете, вероятно, прекрасно, просто воспользуйтесь исключениями. В противном случае быстрый поиск в Google найдет интересные ресурсы для работы с дробями в Java:

http://commons.apache.org/math/userguide/fraction.html

http://www.merriampark.com/fractions.htm

Лучший способ представления доли в Java?

Ответ 9

Обратите внимание, что мы используем компьютер... В компьютере много плунжера, и точность принимает ram. Поэтому, когда вам нужна бесконечная точность, вам нужно (infinite * infinite) ^ (infinite * Integer.MAX_VALUE) terrabyte ram...

Я знаю, что 1 / 3 есть 0.333333..., и его можно сохранить в ram, как "один, разделенный на три", и затем вы можете размножить его, и вы должны иметь 1. Но я не думаю, что у Java есть что-то подобное... Возможно, вам нужно выиграть Нобелевскую премию за то, что она написала что-то такое. ;-)