& # 8220; nuevo BigDecimal (13.3D) & # 8221; resulta impreciso & # 8220; 13.3000000000000007105 .. & # 8221 ;?

StackOverflow https://stackoverflow.com/questions/460755

  •  19-08-2019
  •  | 
  •  

Pregunta

¿Cómo es que el BigDecimal de Java puede ser tan doloroso?

Double d = 13.3D;

BigDecimal bd1 = new BigDecimal(d);
BigDecimal bd2 = new BigDecimal(String.valueOf(d));


System.out.println("RESULT 1: "+bd1.toString());
System.out.println("RESULT 2: "+bd2.toString());

RESULT 1: 13.300000000000000710542735760100185871124267578125
RESULT 2: 13.3

¿Hay alguna situación en la que se desearía el Resultado 1? Sé que Java 1.5 cambió el método toString () , pero ¿fue esta la consecuencia prevista?

También me doy cuenta de que BigDecimal tiene doubleValue () , etc., pero la biblioteca con la que estoy trabajando utiliza un toString () y No puedo cambiar eso :-(

Saludos.

¿Fue útil?

Solución

Bueno, la API aborda esta aparente inconsistencia en el constructor BigDecimal (double val) :

  
      
  1. Los resultados de este constructor pueden ser algo impredecibles. Uno podría   asumir que escribir nuevo   BigDecimal (0.1) en Java crea un   BigDecimal que es exactamente igual a   0.1 (un valor sin escala de 1, con una escala de 1), pero en realidad es igual   a   0.1000000000000000055511151231257827021181583404541015625. Esto se debe a que 0.1 no puede ser   representado exactamente como un doble (o,   para el caso, como una fracción binaria   de cualquier longitud finita). Por lo tanto, el valor   que se pasa a la   constructor no es exactamente igual a   0.1, a pesar de las apariencias.

  2.   
  3. El constructor de cadenas, por otro lado, es perfectamente predecible:   escribir un nuevo BigDecimal (" 0.1 ") crea   un BigDecimal que es exactamente igual a   0.1, como cabría esperar. Por lo tanto, generalmente se recomienda que   El constructor de cadenas se utilizará en   preferencia a este.

  4.   
  5. Cuando se debe usar un doble como fuente para un BigDecimal , tenga en cuenta que   este constructor proporciona un exacto   conversión; no da lo mismo   resultado como convertir el doble a un   Cadena usando el   Método Double.toString (doble) y   luego usando BigDecimal (String)   constructor. Para obtener ese resultado, usa   El valor estático del método (doble) .

  6.   

Moraleja de la historia: el dolor parece autoinfligido, solo use new BigDecimal (String val) o BigDecimal.valueOf (double val) en su lugar =)

Otros consejos

Su problema no tiene nada que ver con BigDecimal , y todo con Double , que no puede representar 13.3 con precisión, ya que utiliza fracciones binarias internamente.

Entonces su error se introduce en la primera línea. El primer BigDecimal simplemente lo conserva, mientras que String.valueOf () realiza un redondeo sospechoso que hace que el segundo tenga el contenido deseado, casi por suerte.

Es posible que desee informarse sobre cómo se implementan los valores de coma flotante ( IEEE 754-1985 ). Y de repente, todo se volverá cristalino.

Esto no es culpa de BigDecimal : es culpa de double . BigDecimal representa con precisión el valor exacto de d . String.valueOf solo muestra el resultado con unos pocos decimales.

Las fracciones representadas con tipos de números binarios (es decir, double , float ) no se pueden almacenar con precisión en esos tipos.

    Double d = 13.3;        
    BigDecimal bdNotOk = new BigDecimal(d);
    System.out.println("not ok: " + bdNotOk.toString());

    BigDecimal bdNotOk2 = new BigDecimal(13.3);
    System.out.println("not ok2: " + bdNotOk2.toString());

    double x = 13.3;
    BigDecimal ok = BigDecimal.valueOf(x);
    System.out.println("ok: " + ok.toString());

    double y = 13.3;
    // pretty lame, constructor's behavior is different from valueOf static method
    BigDecimal bdNotOk3 = new BigDecimal(y);
    System.out.println("not ok3: " + bdNotOk3.toString());

    BigDecimal ok2 = new BigDecimal("13.3");
    System.out.println("ok2: " + ok2.toString());

    Double e = 0.0;
    for(int i = 0; i < 10; ++i) e = e + 0.1; // some fractions cannot be accurately represented with binary
    System.out.println("not ok4: " + e.toString()); // should be 1


    BigDecimal notOk5 = BigDecimal.valueOf(e);
    System.out.println("not ok5: " + notOk5.toString()); // should be 1

    /* 
     * here are some fractions that can be represented exactly in binary:
     * 0.5   = 0.1   = 1 / 2
     * 0.25  = 0.01  = 1 / 4
     * 0.75  = 0.11  = 3 / 4
     * 0.125 = 0.001 = 1 / 8
     */

salida:

not ok: 13.300000000000000710542735760100185871124267578125
not ok2: 13.300000000000000710542735760100185871124267578125
ok: 13.3
not ok3: 13.300000000000000710542735760100185871124267578125
ok2: 13.3
not ok4: 0.9999999999999999
not ok5: 0.9999999999999999

Simplemente use BigDecimal.valueOf(d) o nuevos BigDecimal (s) .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top