Двойной против BigDecimal? - PullRequest
       58

Двойной против BigDecimal?

256 голосов
/ 05 августа 2010

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

Ответы [ 8 ]

381 голосов
/ 05 августа 2010

A BigDecimal - точный способ представления чисел.A Double имеет определенную точность.Работа с двойными значениями различной величины (скажем, d1=1000.0 и d2=0.001) может привести к полному падению 0.001 при суммировании, так как разница в величине очень велика.С BigDecimal этого не произойдет.

Недостаток BigDecimal состоит в том, что он медленнее и немного сложнее программировать алгоритмы таким образом (из-за + - * и/ не перегружен).

Если вы имеете дело с деньгами или нужна точность, используйте BigDecimal.В противном случае Doubles имеют тенденцию быть достаточно хорошими.

Я рекомендую прочитать javadoc из BigDecimal, поскольку они объясняют вещи лучше, чем я здесь:)

141 голосов
/ 13 мая 2016

Мой английский не очень хорош, поэтому я просто напишу простой пример здесь.

    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?;)

43 голосов
/ 14 апреля 2015

Существует два основных отличия от double:

  • Произвольная точность, подобно BigInteger, они могут содержать число произвольной точности и размер
  • Base 10 вместо Base 2, BigDecimalэто n * 10 ^ масштаб, где n - произвольное большое целое число со знаком, а масштаб можно рассматривать как количество цифр для перемещения десятичной точки влево или вправо

Причина, по которой вы должны использовать BigDecimal для денежногорасчет состоит не в том, что он может представлять любое число, а в том, что он может представлять все числа, которые могут быть представлены в десятичном представлении и которые включают в себя практически все числа в денежном мире (вы никогда не переводите 1/3 $ кому-либо).

19 голосов
/ 28 мая 2016

Если вы запишите дробное значение, например 1 / 7, в качестве десятичного значения, вы получите

1/7 = 0.142857142857142857142857142857142857142857...

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

Числа, такие как 1/10 или 1/100, выраженные в виде двоичных чисел с дробной частью, также имеют бесконечное числоцифр после десятичной точки:

1/10 = binary 0.0001100110011001100110011001100110...

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

Десятичные числа (например, BigDecimal), с другой стороны, хранят каждую десятичную цифру как есть.Это означает, что десятичный тип не является более точным, чем двоичный тип с плавающей запятой или тип с фиксированной запятой в общем смысле (например, он не может хранить 1/7 без потери точности), но он более точен для чисел с конечным числомдесятичные цифры, как это часто бывает при расчетах денег.

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

7 голосов
/ 18 июля 2015

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.

5 голосов
/ 20 сентября 2018

Если вы имеете дело с расчетами, существуют законы о том, как вы должны рассчитывать и какую точность вы должны использовать.Если вы потерпите неудачу, вы будете делать что-то незаконное.Единственная реальная причина в том, что битовое представление десятичных регистров не является точным.Проще говоря, Василий - это лучшее объяснение.Чтобы дополнить его пример, вот что происходит:

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)));
}

Дает нам вывод:1010 *

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 
0 голосов
/ 19 марта 2018
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();
        }
}
0 голосов
/ 01 марта 2016

Примитивные числовые типы полезны для хранения отдельных значений в памяти.Но при работе с вычислениями с использованием типов double и float возникают проблемы с округлением. Это происходит потому, что представление в памяти не соответствует точно значению.Например, двойное значение должно занимать 64 бита, но Java не использует все 64 бита. Оно хранит только то, что считает важными частями числа.Таким образом, вы можете получить неправильные значения при добавлении значений типа float или double.Может быть, это видео https://youtu.be/EXxUSz9x7BM объяснит больше

...