¿Doble versus BigDecimal?

Resuelto Truong Ha asked hace 14 años • 8 respuestas

Tengo que calcular algunas variables de punto flotante y mi colega me sugiere usarlas BigDecimalen lugar de doubleya que será más preciso. Pero quiero saber qué es y cómo aprovecharlo al máximo BigDecimal.

Truong Ha avatar Aug 05 '10 16:08 Truong Ha
Aceptado

A BigDecimales una forma exacta de representar números. A Doubletiene cierta precisión. Trabajar con dobles de varias magnitudes (digamos d1=1000.0y d2=0.001) podría dar como resultado que 0.001se eliminen por completo al sumar, ya que la diferencia de magnitud es muy grande. Con BigDecimalesto no pasaría.

La desventaja BigDecimales que es más lento y es un poco más difícil programar algoritmos de esa manera (debido a + - *que /no está sobrecargado).

Si se trata de dinero o la precisión es imprescindible, utilice BigDecimal. De lo contrario, Doublessuelen ser lo suficientemente buenos.

Recomiendo leer el javadoc porque BigDecimalexplican las cosas mejor que yo aquí :)

extraneon avatar Aug 05 '2010 09:08 extraneon

Mi inglés no es bueno, así que escribiré aquí un ejemplo sencillo.

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

Salida del programa:

0.009999999999999998
0.01

¿Alguien todavía quiere usar el doble? ;)

Basil avatar May 13 '2016 19:05 Basil

Hay dos diferencias principales con el doble:

  • Precisión arbitraria, de manera similar a BigInteger, pueden contener un número de precisión y tamaño arbitrarios (mientras que un doble tiene un número fijo de bits)
  • Base 10 en lugar de Base 2, un BigDecimal es n*10^-scaledonde n es un entero grande arbitrario con signo y la escala se puede considerar como el número de dígitos para mover el punto decimal hacia la izquierda o hacia la derecha.

Todavía no es cierto decir que BigDecimal pueda representar cualquier número. Pero dos razones por las que debería utilizar BigDecimal para cálculos monetarios son:

  • Puede representar todos los números que se pueden representar en noción decimal y eso incluye prácticamente todos los números del mundo monetario (nunca transfieres 1/3 $ a alguien).
  • La precisión se puede controlar para evitar errores acumulados. Con a double, a medida que aumenta la magnitud del valor, su precisión disminuye y esto puede introducir un error significativo en el resultado.
Meros avatar Apr 14 '2015 08:04 Meros

Si escribe un valor fraccionario como 1 / 7un valor decimal, obtendrá

1/7 = 0.142857142857142857142857142857142857142857...

con una repetición infinita de los dígitos 142857. Como sólo puedes escribir un número finito de dígitos, inevitablemente introducirás un error de redondeo (o truncamiento).

Los números similares 1/10o 1/100expresados ​​como números binarios con parte fraccionaria también tienen un número infinito de dígitos después del punto decimal:

1/10 = binary 0.0001100110011001100110011001100110...

Doublesalmacena valores como binarios y, por lo tanto, podría introducir un error únicamente al convertir un número decimal en un número binario, sin siquiera hacer ninguna aritmética.

Los números decimales, por otro lado, a menudo almacenan cada dígito decimal tal como está (codificado en binario, pero cada decimal por sí solo) o, en el caso de BigDecimal, como dos números enteros binarios con signo en la forma x*10 y (gracias a @ AlexSalauyou por señalarlo). Ver: Clase BigDecimal . Esto significa que un tipo decimal no es más preciso que un tipo binario de punto flotante o de punto fijo en un sentido general (es decir, no puede almacenarse 1/7sin pérdida de precisión), pero es más preciso para números que tienen un número finito de dígitos decimales como Este suele ser el caso de los cálculos monetarios.

Java BigDecimaltiene la ventaja adicional de que puede tener un número arbitrario (pero finito) de dígitos en ambos lados del punto decimal, limitado únicamente por la memoria disponible.

Olivier Jacot-Descombes avatar May 28 '2016 15:05 Olivier Jacot-Descombes

Si se trata de cálculo, existen leyes sobre cómo se debe calcular y qué precisión se debe utilizar. Si fallas, estarás haciendo algo ilegal. La única razón real es que la representación de bits de los casos decimales no es precisa. Como dijo simplemente Basil, un ejemplo es la mejor explicación. Sólo para complementar su ejemplo, esto es lo que sucede:

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

Producción:

Double:  0,3 - 0,2 = 0.09999999999999998
Float:   0,3 - 0,2 = 0.10000001
BigDec:  0,3 - 0,2 = 0.1

También tenemos eso:

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

Nos da la salida:

Double:  10 / 3 = 3.3333333333333335
Float:   10 / 3 = 3.3333333
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion

Pero:

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

Tiene la salida:

BigDec:  10 / 3 = 3.3333 
jfajunior avatar Sep 20 '2018 08:09 jfajunior