2012-03-29 2 views
1

Tout en répondant à this question, je suis ces résultats confus:Double confusion de précision?

double d = 0.49999999999999990d; //output 0.4999999999999999 as expected 
d = 0.49999999999999991d; //output 0.4999999999999999 
d = 0.49999999999999992d; //output 0.49999999999999994 
d = 0.49999999999999993d; //output 0.49999999999999994 
d = 0.49999999999999994d; //output 0.49999999999999994 as expected 
d = 0.49999999999999995d; //output 0.49999999999999994 
d = 0.49999999999999996d; //output 0.49999999999999994 
d = 0.49999999999999997d; //output 0.49999999999999994 
d = 0.49999999999999998d; //output 0.5 

Pourquoi ce comportement montrant?

REMARQUE: J'ai obtenu ces sorties en imprimant d; Je veux dire que j'ai utilisé:

System.out.println(d); 
+2

duplication possible de [Conserver la précision avec Double dans java] (http://stackoverflow.com/questions/322749/retain-precision-with-doubles-in-java) –

Répondre

2

Les types de points flottants ne peuvent pas représenter exactement tous les nombres réels. En fait, un double est un type à virgule flottante de 64 bits, et ne peut donc représenter que 2 valeurs différentes ... et il y a un nombre infini de nombres réels. (En effet, il existe un nombre infini de nombres réels entre 0.49999999999999990d et 0.49999999999999999d.)

Vous avez choisi quelques chiffres qui se situent entre les valeurs consécutives dans le jeu-de-toutes double valeurs. En d'autres termes, vous avez dépassé les limites de précision pour le type double.

Que pouvez-vous faire à ce sujet? Eh bien, une façon d'obtenir plus de précision est d'utiliser la classe BigDecimal, qui peut (en théorie) vous donner dans la région de 2 milliards de chiffres décimaux de précision. L'inconvénient est que votre code sera plus compliqué ... et considérablement plus lent, en fonction de la précision que vous utilisez.

L'autre approche consiste à reconnaître que vous n'avez probablement pas besoin de beaucoup de précision.

0

Seuls certains nombres peuvent être représentés exactement comme doubles. Il y a trois chiffres dans la gamme considérée:

  • 0.49999999999999990
  • 0.49999999999999994
  • 0.5

Tout entre ces chiffres s'arrondi au plus proche des trois.

Si vous regardez la façon dont ces doubles sont représentées dans l'hexagone, vous verrez que les trois numéros ont mantisses consécutifs (la partie avant la p):

In [20]: float.hex(0.49999999999999990) 
Out[20]: '0x1.ffffffffffffep-2' 

In [21]: float.hex(0.49999999999999994) 
Out[21]: '0x1.fffffffffffffp-2' 

In [22]: float.hex(0.5) 
Out[22]: '0x1.0000000000000p-1' 

nombres représentant tels que 0.49999999999999992 aurait exactement besoin plus de bits de mantisse que double peut offrir.

1

System.out.println(d) va passer par Double.toString ce qui est une méthode assez complexe (comme on le voit dans sa documentation) et ne se comportera pas toujours comme on s'y attendrait. Il donne essentiellement la chaîne la plus courte qui détermine de façon unique d.

Peut-être la sortie de ce programme précise ceci:

double[] tests = { 
     0.49999999999999990d, //output 0.4999999999999999 as expected 
     0.49999999999999991d, //output 0.4999999999999999 
     0.49999999999999992d, //output 0.49999999999999994 
     0.49999999999999993d, //output 0.49999999999999994 
     0.49999999999999994d, //output 0.49999999999999994 as expected 
     0.49999999999999995d, //output 0.49999999999999994 
     0.49999999999999996d, //output 0.49999999999999994 
     0.49999999999999997d, //output 0.49999999999999994 
     0.49999999999999998d, //output 0.5 
    }; 

String[] literals = { 
     "0.49999999999999990d", 
     "0.49999999999999991d", 
     "0.49999999999999992d", 
     "0.49999999999999993d", 
     "0.49999999999999994d", 
     "0.49999999999999995d", 
     "0.49999999999999996d", 
     "0.49999999999999997d", 
     "0.49999999999999998d", 
    }; 

String f = "%-25s%-65s%-25s%n"; 
System.out.printf(f, "Literal", "Actually represents", "Printed as"); 

for (int i = 0; i < tests.length; i++) 
    System.out.printf(f, literals[i], 
         new BigDecimal(tests[i]).toString(), 
         Double.valueOf(tests[i])); 

Sortie:

Literal     Actually represents            Printed as    
0.49999999999999990d  0.49999999999999988897769753748434595763683319091796875   0.4999999999999999  
0.49999999999999991d  0.49999999999999988897769753748434595763683319091796875   0.4999999999999999  
0.49999999999999992d  0.499999999999999944488848768742172978818416595458984375   0.49999999999999994  
0.49999999999999993d  0.499999999999999944488848768742172978818416595458984375   0.49999999999999994  
0.49999999999999994d  0.499999999999999944488848768742172978818416595458984375   0.49999999999999994  
0.49999999999999995d  0.499999999999999944488848768742172978818416595458984375   0.49999999999999994  
0.49999999999999996d  0.499999999999999944488848768742172978818416595458984375   0.49999999999999994  
0.49999999999999997d  0.499999999999999944488848768742172978818416595458984375   0.49999999999999994  
0.49999999999999998d  0.5                0.5      

Comme on le voit, le littéral est parfois loin de la valeur qu'il représente en fait, ce qui signifie que Double.toString imprime quelque chose qui peut paraître surprenant.

Questions connexes