BigDecimal.divide (...) - соответствующий масштаб для отношений с не заканчивающимися расширениями - PullRequest
1 голос
/ 19 декабря 2011

Я использую BigDecimal для некоторой математики с плавающей точкой.Если вы поделите 5 на 4.2, вы получите исключение (так как в результате получится нескончаемое расширение, которое не может быть представлено BigDecimal), т.е.

BigDecimal five = new BigDecimal("5");
BigDecimal fourPointTwo = new BigDecimal("4.2");
five.divide(fourPointTwo) // ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

Я готовпотерять некоторую точность в этом случае, поэтому я собираюсь использовать метод divide(...), который позволяет получить шкалу для результата:

five.divide(fourPointTwo, 2, RoundingMode.HALF_UP); //Fine, but obviously not 100% accurate

Какую шкалу я должен передать этому методу, чтобырезультат такой же точный, как если бы я выполнил вычисление, используя два doubles?

Ответы [ 2 ]

2 голосов
/ 19 декабря 2011

Из javadocs класса BigDecimal :

Если ноль или положительный, шкала - это число цифр справа от десятичной точки.Если значение отрицательное, немасштабированное значение числа умножается на десять до степени отрицания шкалы.Следовательно, значение числа, представленного BigDecimal, равно (unscaledValue × 10 -scale ).

Точность double изменяется в соответствии с порядком величины значения,Согласно this , он использует 52 бита для хранения неподписанной мантиссы, поэтому любое целое число, которое может быть представлено 52 битами, будет в порядке.Это будет примерно 18 десятичных цифр.

Далее, double использует 11 бит для хранения показателя степени.Итак, что-то вроде 4 десятичной точности.Таким образом, может быть представлено любое целое число до 52 бит, умноженное на положительную или отрицательную степень 2 с максимум 10 битами (один бит является знаком ожидаемого значения).Кроме того, вы начинаете терять точность.

Дополнительный бит double хранит знак.

Таким образом, шкала 18 + 4 = 22 будет, по крайней мере, такой же точной, как double.

1 голос
/ 19 декабря 2011

Ваша проблема называется «ошибка округления» или «ошибка округления». Пример:

У вас есть два числа a и b. Вы знаете, что каждая имеет определенную точность (то есть количество цифр, в которых вы уверены), что означает, что каждая другая цифра является "случайным" шумом.

Представьте себе, b имеет точность двух цифр. Результат (b*100)-int(b*100) будет случайным, поскольку операция обрезает все «правильные» цифры.

Эти ошибки распространяются в зависимости от математической операции. Некоторые примеры:

  • Поля ошибок добавляются при добавлении чисел. Если a и b имеют точность два, добавление их может превратить вторую цифру дроби в мусор: 0,003 + 0,008 = 0,011

  • Умножение быстро увеличивает ошибку, а экспоненциальные функции увеличивают ее еще быстрее.

  • Деление уменьшает погрешность (0,003 / 3 = 0,001)

Таким образом, если вы хотите получить правильный ответ, вы должны вычислить границы ошибок всех операций в вашем коде, следуя правилам, изложенным выше. Связать кого-нибудь?

Конечно, это обычно не вариант. Поэтому вам нужно подумать, с какой ошибкой вы можете жить. Например, если вы выполняете математические операции с финансовыми данными, обычно достаточно точности 10 или 20, потому что у вас достаточно битов, чтобы «потратить» их на несколько математических операций, прежде чем ошибка перерастет в значительную часть значения.

Пример: вы начинаете с 10.500 000 000 и 3.100 000 000. Если вы разделите два, вы получите 3.387 096 774. Из этого вам понадобится только 3.87 - остальное - запасная точность, которую вы можете использовать в дальнейших операциях, пока вы не округлите последний результат до двух цифр и не сохраните его обратно в базе данных.

...