Если вы не можете избежать синтаксического анализа в примитиве double
или хранить его как double, вам следует преобразовать в BigDecimal
как можно раньше.
double
не может точно представлять десятичные дроби.Значение в double x = 7.3;
никогда не будет ровно 7,3, но что-то очень очень близкое к нему, с разницей, видимой от 16-й цифры или около того справа (давая 50 десятичных знаков или около того).Не вводите в заблуждение тот факт, что печать может дать ровно «7,3», поскольку печать уже выполняет какое-то округление и не показывает точное число.
Если вы выполняете много вычислений с double
числа, крошечные различия в конечном итоге будут суммироваться, пока они не превысят вашу терпимость.Поэтому использование двойных чисел в вычислениях, где требуются десятичные дроби, действительно является бомбой замедленного действия.
[...] мы ожидаем, что значения не превышают всего 10 цифр, 5 цифр дроби.
Я прочитал это утверждение, чтобы означать, что все числа, с которыми вы имеете дело, должны быть точными кратными 0.00001
, без каких-либо дополнительных цифр.Вы можете преобразовать двойные числа в такие BigDecimals с помощью
new BigDecimal.valueOf(Math.round(doubleVal * 100000), 5)
. Это даст вам точное представление числа с 5 десятичными дробными цифрами, 5-значными дробями, ближайшим к входу doubleVal
.Таким образом, вы исправляете крошечные различия между doubleVal
и десятичным числом, которое вы изначально имели в виду.
Если вы просто используете BigDecimal.valueOf(double val)
, вы пройдете через строковое представление двойного васвы используете, который не может гарантировать, что это то, что вы хотите.Это зависит от процесса округления внутри класса Double, который пытается представить двойное приближение 7,3 (возможно, 7,30000000000000123456789123456789125) с наиболее вероятным числом десятичных цифр.В результате получается «7.3» (и, слава разработчикам, довольно часто совпадает с «ожидаемой» строкой), а не «7.300000000000001» или «7.3000000000000012», которые кажутся мне одинаково правдоподобными.
Вот почемуЯ рекомендую не полагаться на это округление, а выполнить округление самостоятельно, сдвинув десятичное число на 5 позиций, затем округлив до ближайшего длинного и построив BigDecimal, уменьшенное на 5 десятичных знаков.Это гарантирует, что вы получите точное значение с (не более) 5 дробными десятичными разрядами.
Затем выполните вычисления с BigDecimals (используя соответствующий MathContext для округления, если необходимо).
Когданаконец, вам нужно сохранить число как двойное, используйте BigDecimal.doubleValue()
.Результирующее двойное число будет достаточно близко к десятичному знаку, так что вышеупомянутое преобразование, несомненно, даст вам тот же BigDecimal, который у вас был раньше (если только у вас нет действительно огромных чисел, таких как 10 цифр перед десятичной точкой - вы потерялись с double
в любом случае).
PS Обязательно используйте BigDecimal
, только если дроби десятичные относятся к вам - были времена, когда валюта британского шиллинга состояла из двенадцати пенсов.Представление дробных фунтов в виде BigDecimal
даст катастрофу намного хуже, чем использование двойных.