Я должен вычислить некоторые переменные с плавающей точкой, и мой коллега предлагает мне использовать BigDecimal
вместо, double
так как это будет более точным. Но я хочу знать, что это такое и как извлечь из этого максимум BigDecimal
?
Я должен вычислить некоторые переменные с плавающей точкой, и мой коллега предлагает мне использовать BigDecimal
вместо, double
так как это будет более точным. Но я хочу знать, что это такое и как извлечь из этого максимум BigDecimal
?
Ответы:
A BigDecimal
является точным способом представления чисел. А Double
имеет определенную точность. Работа с двойными значениями различной величины (скажем, d1=1000.0
и d2=0.001
) может привести к 0.001
полному падению при суммировании, поскольку разница в величине очень велика. С BigDecimal
этим бы не случилось.
Недостатком BigDecimal
является то , что он медленнее, и это немного сложнее программных алгоритмов , что путь (из - за +
-
*
и /
не перегружена).
Если вы имеете дело с деньгами, или точность является обязательным, используйте BigDecimal
. В противном случае, Doubles
как правило, достаточно хорошо.
Я рекомендую читать Javadoc о , BigDecimal
как они объясняют вещи лучше , чем я здесь :)
if (Math.abs(loadPerServer - maxLoadPerServer) < 0.000001d) {
BigDecimal
», у Double будет больше «точности» (больше цифр).
Мой английский не очень хороший, поэтому я просто напишу простой пример здесь.
double a = 0.02;
double b = 0.03;
double c = b - a;
System.out.println(c);
BigDecimal _a = new BigDecimal("0.02");
BigDecimal _b = new BigDecimal("0.03");
BigDecimal _c = _b.subtract(_a);
System.out.println(_c);
Выход программы:
0.009999999999999998
0.01
Кто-нибудь еще хочет использовать double? ;)
System.out.println(0.003f - 0.002f);
BigDecimal является точным:System.out.println(new BigDecimal("0.003").subtract(new BigDecimal("0.002")));
Есть два основных отличия от двойного:
Причина, по которой вы должны использовать BigDecimal для денежных расчетов, заключается не в том, что он может представлять любое число, а в том, что он может представлять все числа, которые могут быть представлены в десятичном представлении и которые включают в себя практически все числа в денежном мире (вы никогда не переводите 1/3 $ кому-то).
Если вы записываете дробное значение, такое 1 / 7
как десятичное значение, вы получите
1/7 = 0.142857142857142857142857142857142857142857...
с бесконечной последовательностью 142857
. Поскольку вы можете записать только конечное число цифр, вы неизбежно внесете ошибку округления (или усечения).
Числа, подобные 1/10
или 1/100
выраженные в виде двоичных чисел с дробной частью, также имеют бесконечное количество цифр после десятичной точки:
1/10 = binary 0.0001100110011001100110011001100110...
Doubles
хранить значения в двоичном виде и, следовательно, может привести к ошибке исключительно путем преобразования десятичного числа в двоичное число, даже не выполняя никакой арифметики.
С BigDecimal
другой стороны, десятичные числа (как ) сохраняют каждую десятичную цифру как есть. Это означает, что десятичный тип не является более точным, чем двоичный тип с плавающей запятой или тип с фиксированной запятой в общем смысле (т. Е. Он не может быть сохранен 1/7
без потери точности), но он более точен для чисел с конечным числом десятичных разрядов, так как это часто бывает для расчета денег.
У Java BigDecimal
есть дополнительное преимущество, заключающееся в том, что он может иметь произвольное (но конечное) количество цифр с обеих сторон десятичной точки, ограниченное только доступной памятью.
BigDecimal - это числовая библиотека Oracle с произвольной точностью. BigDecimal является частью языка Java и полезен для самых разных приложений - от финансовых до научных (вот где я).
Нет ничего плохого в использовании двойников для определенных вычислений. Предположим, однако, что вы хотите вычислить Math.Pi * Math.Pi / 6, то есть значение дзета-функции Римана для реального аргумента два (проект, над которым я сейчас работаю). Деление с плавающей точкой ставит вас перед болезненной проблемой ошибки округления.
BigDecimal, с другой стороны, включает в себя множество опций для вычисления выражений с произвольной точностью. Методы добавления, умножения и деления, описанные в документации Oracle ниже, «заменяют» +, * и / в BigDecimal Java World:
http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
Метод CompareTo особенно полезен в циклах while и for.
Однако будьте осторожны при использовании конструкторов для BigDecimal. Конструктор строк очень полезен во многих случаях. Например, код
BigDecimal onethird = new BigDecimal ("0.33333333333");
использует строковое представление 1/3 для представления этого бесконечно повторяющегося числа с заданной степенью точности. Ошибка округления, скорее всего, где-то так глубоко внутри JVM, что ошибки округления не будут мешать большинству ваших практических расчетов. Однако, по личному опыту, я видел, как округление приближается. В этом отношении метод setScale важен, как видно из документации Oracle.
/* * Portions Copyright IBM Corporation, 2001. All Rights Reserved. */
Если вы имеете дело с расчетами, существуют законы о том, как вы должны рассчитывать и какую точность вы должны использовать. Если вам не удастся, вы будете делать что-то незаконное. Единственная реальная причина в том, что битовое представление десятичных регистров не является точным. Как сказал Бэзил, пример - лучшее объяснение. Просто чтобы дополнить его пример, вот что происходит:
static void theDoubleProblem1() {
double d1 = 0.3;
double d2 = 0.2;
System.out.println("Double:\t 0,3 - 0,2 = " + (d1 - d2));
float f1 = 0.3f;
float f2 = 0.2f;
System.out.println("Float:\t 0,3 - 0,2 = " + (f1 - f2));
BigDecimal bd1 = new BigDecimal("0.3");
BigDecimal bd2 = new BigDecimal("0.2");
System.out.println("BigDec:\t 0,3 - 0,2 = " + (bd1.subtract(bd2)));
}
Вывод:
Double: 0,3 - 0,2 = 0.09999999999999998
Float: 0,3 - 0,2 = 0.10000001
BigDec: 0,3 - 0,2 = 0.1
Также у нас есть что:
static void theDoubleProblem2() {
double d1 = 10;
double d2 = 3;
System.out.println("Double:\t 10 / 3 = " + (d1 / d2));
float f1 = 10f;
float f2 = 3f;
System.out.println("Float:\t 10 / 3 = " + (f1 / f2));
// Exception!
BigDecimal bd3 = new BigDecimal("10");
BigDecimal bd4 = new BigDecimal("3");
System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4)));
}
Дает нам вывод:
Double: 10 / 3 = 3.3333333333333335
Float: 10 / 3 = 3.3333333
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion
Но:
static void theDoubleProblem2() {
BigDecimal bd3 = new BigDecimal("10");
BigDecimal bd4 = new BigDecimal("3");
System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4, 4, BigDecimal.ROUND_HALF_UP)));
}
Имеет выход:
BigDec: 10 / 3 = 3.3333