ArithmeticException брошен во время BigDecimal.divide

Я думал java.math.BigDecimal как предполагается, Answer™ к потребности выполнения бесконечной арифметики точности с десятичными числами.

Рассмотрите следующий отрывок:

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 вообще?

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

33
задан polygenelubricants 1 May 2010 в 09:03
поделиться

8 ответов

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

Затем я учился в начальной школе 1 , они научили меня, что когда вы делите на 1 на 3, вы получаете 0,33333 ... то есть повторяющуюся десятичную дробь. Деление чисел, представленных в десятичной форме, НЕ является точным. Фактически, для любой фиксированной базы будут дроби (результат деления одного целого числа на другое), которые не могут быть представлены точно как число с плавающей запятой конечной точности в этой базе. (Число будет иметь повторяющуюся часть ...)

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

В документации Javadoc BigDecimal говорится следующее:

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

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

РЕДАКТИРОВАТЬ - в ответ на эти последующие запросы от OP.

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

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

Он должен отслеживать и обнаруживать цикл в дивиденде. МОЖНО было выбрать другой способ обработки, путем отметки повторяющейся части и т. Д.

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

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


1 - Это была отличная начальная школа ...

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

23
ответ дан 27 November 2019 в 18:25
поделиться

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

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

a/b + c

равно

(a + bc) / b.
6
ответ дан 27 November 2019 в 18:25
поделиться

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

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

9
ответ дан 27 November 2019 в 18:25
поделиться

Есть ли потребность в

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

resulting in

b==1;

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

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

2
ответ дан 27 November 2019 в 18:25
поделиться

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

Двойные, плавающие числа не подходят для использования в финансовых приложениях, поскольку они не могут точно представлять дроби, равные 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, вы не найдете его там.

3
ответ дан 27 November 2019 в 18:25
поделиться

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

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

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

5
ответ дан 27 November 2019 в 18:25
поделиться

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

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

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

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

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

1
ответ дан 27 November 2019 в 18:25
поделиться

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

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

0
ответ дан 27 November 2019 в 18:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: