& # 8220; nuovo BigDecimal (13.3D) & # 8221; risulta impreciso & # 8220; 13.3000000000000007105 .. & # 8221 ;?

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

  •  19-08-2019
  •  | 
  •  

Domanda

In che modo il BigDecimal di Java può essere così 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

C'è qualche situazione in cui si vorrebbe il risultato 1? So che Java 1.5 ha cambiato il metodo inString () , ma è stata questa la conseguenza prevista?

Inoltre mi rendo conto che BigDecimal ha doubleValue () ecc., ma la libreria con cui sto lavorando utilizza utilmente un toString () e Non posso cambiarlo :-(

Saluti.

È stato utile?

Soluzione

Bene, l'API API risolve questa apparente incoerenza nel costruttore BigDecimal (double val) :

  
      
  1. I risultati di questo costruttore possono essere in qualche modo imprevedibili. Uno potrebbe   supponiamo che scrivere di nuovo   BigDecimal (0.1) in Java crea un   BigDecimal che è esattamente uguale a   0.1 (un valore non scalato di 1, con una scala di 1), ma in realtà è uguale   a   0,1000000000000000055511151231257827021181583404541015625. Questo perché 0.1 non può essere   rappresentato esattamente come un doppio (o,   del resto, come una frazione binaria   di qualsiasi lunghezza finita). Quindi, il valore   che viene passato al   costruttore non è esattamente uguale a   0.1, nonostante le apparenze.

  2.   
  3. Il costruttore di String, d'altra parte, è perfettamente prevedibile:   la creazione di nuovi BigDecimal ("0,1") crea   un BigDecimal che è esattamente uguale a   0,1, come ci si aspetterebbe. Pertanto, è generalmente raccomandato che il file   Costruttore di stringhe da utilizzare in   preferenza a questo.

  4.   
  5. Quando un doppio deve essere usato come fonte per un BigDecimal , nota che   questo costruttore fornisce un esatto   conversione; non dà lo stesso   risultato come convertire il doppio in a   String usando il   Metodo Double.toString (doppio) e   quindi utilizzando BigDecimal (String)   costruttore. Per ottenere quel risultato, usa   il metodo valueOf statico (doppio) .

  6.   

Morale della storia: il dolore sembra autoinflitto, basta usare new BigDecimal (String val) o BigDecimal.valueOf (double val) invece =)

Altri suggerimenti

Il tuo problema non ha nulla a che fare con BigDecimal e tutto con Double , che non può rappresentare 13.3 in modo accurato, poiché utilizza frazioni binarie internamente.

Quindi il tuo errore viene introdotto nella prima riga. Il primo BigDecimal semplicemente lo preserva, mentre String.valueOf () esegue alcuni arrotondamenti di pesce che fanno sì che il secondo abbia il contenuto desiderato, praticamente per fortuna.

Potresti voler informarti su come vengono implementati i valori in virgola mobile ( IEEE 754-1985 ). E improvvisamente, tutto diventerà cristallino.

Non è colpa di BigDecimal - è colpa di double . BigDecimal rappresenta accuratamente il valore esatto di d . String.valueOf mostra solo il risultato con alcune cifre decimali.

Le frazioni rappresentate con tipi di numero binario (ad es. double , float ) non possono essere archiviate accuratamente in questi tipi.

    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
     */

uscita:

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

Usa semplicemente BigDecimal.valueOf(d) o nuovi BigDecimal (s) .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top