& # 8220; new BigDecimal (13.3D) & # 8221; imprécise & # 8220; 13.3000000000000007105 .. & # 8221 ;?

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

  •  19-08-2019
  •  | 
  •  

Question

Comment se fait-il que le BigDecimal de Java puisse être aussi douloureux?

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

Existe-t-il une situation dans laquelle le résultat 1 serait souhaité? Je sais que Java 1.5 a modifié la méthode toString () mais était-ce la conséquence escomptée?

Je réalise aussi que BigDecimal a doubleValue () , etc., mais la bibliothèque avec laquelle je travaille utilise utilement un toString () et Je ne peux pas changer ça: - (

A bientôt.

Était-ce utile?

La solution

Eh bien, le API corrige cette incohérence apparente dans le constructeur BigDecimal (double valeur) :

  
      
  1. Les résultats de ce constructeur peuvent être quelque peu imprévisibles. On pourrait   suppose que l'écriture nouvelle   BigDecimal (0.1) en Java crée un   BigDecimal qui est exactement égal à   0,1 (une valeur non échelonnée de 1, avec une échelle de 1), mais elle est en réalité égale   à   0.1000000000000000055511151231257827021181583404541015625. En effet, 0,1 ne peut pas être   représenté exactement comme un double (ou,   d'ailleurs, en tant que fraction binaire   de toute longueur finie). Ainsi, la valeur   qui est transmis à la   constructeur n'est pas exactement égal à   0,1 malgré les apparences.

  2.   
  3. Le constructeur String, quant à lui, est parfaitement prévisible:   L’écriture de nouvelles données BigDecimal ("0.1") crée   un BigDecimal qui est exactement égal à   0,1, comme on pourrait s'y attendre. Par conséquent, il est généralement recommandé que le   Constructeur de chaîne être utilisé dans   préférence pour celui-ci.

  4.   
  5. Lorsqu'un double doit être utilisé comme source d'un BigDecimal , notez que   ce constructeur fournit une exacte   conversion; ça ne donne pas le même   résultat en convertissant le double en un   String en utilisant le   Méthode Double.toString (double) et   puis en utilisant le BigDecimal (String)   constructeur. Pour obtenir ce résultat, utilisez   la méthode statique valueOf (double) .

  6.   

Morale de l’histoire: la douleur semble s’auto-infliger, il suffit d’utiliser new BigDecimal (String val) ou BigDecimal.valueOf (double valeur) à la place =)

Autres conseils

Votre problème n'a rien à voir avec BigDecimal et tout avec Double , qui ne peut pas représenter 13.3 avec précision, car il utilise des fractions binaires en interne.

Votre erreur est donc introduite à la toute première ligne. Le premier BigDecimal le conserve tout simplement, alors que String.valueOf () effectue des arrondis ludiques qui donnent au second contenu le contenu souhaité, plutôt par chance.

Vous voudrez peut-être vous informer sur la façon dont les valeurs en virgule flottante sont implémentées ( IEEE 754-1985 ). Et tout à coup, tout deviendra limpide.

Ce n'est pas la faute de BigDecimal - c'est la faute de double . BigDecimal représente avec précision la valeur exacte de d . String.valueOf n'affiche que le résultat à quelques décimales près.

Les fractions représentées avec des types de nombres binaires (c'est-à-dire double , float ) ne peuvent pas être stockées avec précision dans ces types.

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

sortie:

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

Utilisez simplement BigDecimal.valueOf (d) ou nouveau BigDecimal (s) .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top