Мне нужно вычислить некоторые переменные с плавающей запятой, и мой коллега предложил мне использовать BigDecimal
вместо double
, так как это будет более точно. Но я хочу знать, что это такое и как извлечь максимум из BigDecimal
?
Двойной против BigDecimal?
Ответ 1
A BigDecimal
- это точный способ представления чисел. A Double
имеет определенную точность. Работа с удвоениями разных величин (например, d1=1000.0
и d2=0.001
) может привести к тому, что 0.001
будет выпадать все вместе при суммировании, поскольку разница в величине будет настолько большой. С BigDecimal
этого не произойдет.
Недостатком BigDecimal
является то, что он медленнее, и немного сложнее запрограммировать алгоритмы таким образом (из-за +
-
*
и /
не перегружается).
Если вы имеете дело с деньгами или точность является обязательной, используйте BigDecimal
. В противном случае Doubles
имеют тенденцию быть достаточно хорошими.
Я рекомендую прочитать javadoc BigDecimal
, поскольку они объясняют вещи лучше, чем я здесь:)
Ответ 2
Мой английский не очень хорошо, поэтому я просто напишу простой пример.
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?;)
Ответ 3
Есть два основных отличия от двойных:
- Произвольная точность, подобно BigInteger, может содержать количество произвольной точности и размера
- База 10 вместо Base 2, BigDecimal имеет размер n * 10 ^, где n - произвольное большое целое число со знаком, а масштаб можно рассматривать как число цифр для перемещения десятичной точки влево или вправо
Причина, по которой вы должны использовать BigDecimal для денежных расчетов, заключается не в том, что она может представлять любое число, а в том, что она может представлять все числа, которые могут быть представлены десятичным понятием и которые включают практически все числа в денежном мире (вы никогда не переносите 1/3 $кому-то).
Ответ 4
Если вы запишите дробное значение, например 1 / 7
, в качестве десятичного значения, вы получите
1/7 = 0.142857142857142857142857142857142857142857...
с бесконечной последовательностью 142857
. Поскольку вы можете записать только конечное число цифр, вы неизбежно внесете ошибку округления (или усечения).
Числа, такие как 1/10
или 1/100
, выраженные в виде двоичных чисел с дробной частью, также имеют бесконечное число цифр после десятичной точки:
1/10 = binary 0.0001100110011001100110011001100110...
Doubles
сохраняет значения в двоичном виде и, следовательно, может привести к ошибке исключительно путем преобразования десятичного числа в двоичное число, даже не выполняя никакой арифметики.
Десятичные числа (такие как BigDecimal
), с другой стороны, хранят каждую десятичную цифру как есть. Это означает, что десятичный тип не является более точным, чем двоичный тип с плавающей запятой или тип с фиксированной запятой в общем смысле (то есть он не может хранить 1/7
без потери точности), но он более точен для чисел с конечным числом десятичные цифры, как это часто бывает при расчетах денег.
Java BigDecimal
имеет дополнительное преимущество, заключающееся в том, что он может иметь произвольное (но конечное) количество цифр с обеих сторон десятичной точки, ограниченное только доступной памятью.
Ответ 5
BigDecimal - это произвольная вычислительная библиотека произвольной точности Oracle. BigDecimal является частью языка Java и полезен для различных приложений, от финансового до научного (что там вроде am).
Нет ничего плохого в использовании удвоений для некоторых вычислений. Предположим, однако, что вы хотели вычислить Math.Pi * Math.Pi/6, то есть значение Zeta Function Римана для реального аргумента двух (проект, над которым я сейчас работаю). Разделение с плавающей запятой представляет вам болезненную проблему ошибки округления.
BigDecimal, с другой стороны, включает в себя множество опций для вычисления выражений с произвольной точностью. Методы добавления, умножения и деления, описанные в документации Oracle ниже, "заменяются" на +, * и/в BigDecimal Java World:
http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
Метод compareTo особенно полезен для while и для циклов.
Будьте осторожны, однако, при использовании конструкторов для BigDecimal. Конструктор строк очень полезен во многих случаях. Например, код
BigDecimal onethird = new BigDecimal ( "0.33333333333" );
использует строковое представление 1/3 для представления этого бесконечно повторяющегося числа с определенной степенью точности. Ошибка округления, скорее всего, где-то так глубоко внутри JVM, что ошибки округления не будут нарушать большинство ваших практических вычислений. Однако, по личному опыту, видимый округлый ползание вверх. Метод setScale важен в этом отношении, как видно из документации Oracle.
Ответ 6
Если вы имеете дело с расчетами, существуют законы о том, как вы должны рассчитывать и какую точность вы должны использовать. Если вы потерпите неудачу, вы будете делать что-то незаконное. Единственная реальная причина заключается в том, что представление бит десятичных чисел неточно. Как просто добавляет Василий, примером является лучшее объяснение. Чтобы дополнить его пример, вот что происходит:
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
Ответ 7
package j2ee.java.math;
/**
* Generated from IDL definition of "valuetype "BigDecimal""
* TomORB IDL compiler v1.0
*/
public abstract class BigDecimal extends j2ee.java.lang.Number implements org.omg.CORBA.portable.StreamableValue, j2ee.java.lang.Comparable
{
private String[] _truncatable_ids = {"RMI:java.math.BigDecimal:11F6D308F5398BBD:54C71557F981284F"};
protected int scale_;
protected j2ee.java.math.BigInteger intVal;
/* constants */
int ROUND_UP = 0;
int ROUND_DOWN = 1;
int ROUND_CEILING = 2;
int ROUND_FLOOR = 3;
int ROUND_HALF_UP = 4;
int ROUND_HALF_DOWN = 5;
int ROUND_HALF_EVEN = 6;
int ROUND_UNNECESSARY = 7;
public abstract int _hashCode();
public abstract int scale();
public abstract int signum();
public abstract boolean _equals(org.omg.CORBA.Any arg0);
public abstract java.lang.String _toString();
public abstract j2ee.java.math.BigDecimal abs();
public abstract j2ee.java.math.BigDecimal negate();
public abstract j2ee.java.math.BigDecimal movePointLeft(int arg0);
public abstract j2ee.java.math.BigDecimal movePointRight(int arg0);
public abstract j2ee.java.math.BigDecimal setScale(int arg0);
public abstract j2ee.java.math.BigDecimal setScale(int arg0, int arg1);
public abstract j2ee.java.math.BigDecimal valueOf(long arg0);
public abstract j2ee.java.math.BigDecimal valueOf(long arg0, int arg1);
public abstract int compareTo(j2ee.java.math.BigDecimal arg0);
public abstract j2ee.java.math.BigInteger toBigInteger();
public abstract j2ee.java.math.BigInteger unscaledValue();
public abstract j2ee.javax.rmi.CORBA.ClassDesc classU0024(java.lang.String arg0);
public abstract j2ee.java.math.BigDecimal add(j2ee.java.math.BigDecimal arg0);
public abstract j2ee.java.math.BigDecimal max(j2ee.java.math.BigDecimal arg0);
public abstract j2ee.java.math.BigDecimal min(j2ee.java.math.BigDecimal arg0);
public abstract j2ee.java.math.BigDecimal multiply(j2ee.java.math.BigDecimal arg0);
public abstract j2ee.java.math.BigDecimal subtract(j2ee.java.math.BigDecimal arg0);
public abstract j2ee.java.math.BigDecimal divide(j2ee.java.math.BigDecimal arg0, int arg1);
public abstract j2ee.java.math.BigDecimal divide(j2ee.java.math.BigDecimal arg0, int arg1, int arg2);
public void _write (org.omg.CORBA.portable.OutputStream os)
{
super._write( os );
os.write_long(scale_);
((org.omg.CORBA_2_3.portable.OutputStream)os).write_value( new java.lang.String("intVal") );
}
public void _read (final org.omg.CORBA.portable.InputStream os)
{
super._read( os );
scale_=os.read_long();
intVal=(j2ee.java.math.BigInteger)((org.omg.CORBA_2_3.portable.InputStream)os).read_value ( "RMI:java.math.BigInteger:E2F79B6E7A470003:8CFC9F1FA93BFB1D".toString() );
}
public String[] _truncatable_ids()
{
return _truncatable_ids;
}
public org.omg.CORBA.TypeCode _type()
{
return j2ee.java.math.BigDecimalHelper.type();
}
}
Ответ 8
Примитивные числовые типы полезны для хранения одиночных значений в памяти. Но при рассмотрении вычислений с использованием типов double и float возникают проблемы с округлением. Это происходит потому, что представление памяти не точно соответствует значению. Например, двойное значение должно принимать 64 бита, но Java не использует все 64 бита. Он сохраняет только то, что он считает важными частями числа. Таким образом, вы можете прийти к неправильным значениям при добавлении значений вместе с поплавком или двойным типом. Возможно, это видео https://youtu.be/EXxUSz9x7BM будет объяснять больше