2014-09-02 1 views
2

I c'est le code qui arrondit 62 à 61 et le montre dans la sortie. Pourquoi il décide de tourner et comment obtenir 62 dans la sortie?double arrondi

var d: double; 
i: integer; 
begin 
    d:=0.62; 
    i:= trunc(d*100); 
    Showmessage(inttostr(i)); 

end; 
+0

Copie possible de [Est-ce que le calcul à virgule flottante est rompu?] (Http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – Mark

Répondre

13

Cela se résume au fait que 0.62 n'est pas exactement représentable dans un type de données à virgule flottante binaire. Le closest representable double value to 0.62 est:

 
0.61999 99999 99999 99555 91079 01499 37383 83054 73327 63671 875 

Lorsque vous multipliez cette valeur par 100, la valeur résultante est légèrement inférieure à 62. L'étape suivante dépend de la façon dont est traitée la valeur intermédiaire d*100. Dans votre programme, sous le compilateur Windows 32 bits avec les paramètres par défaut, la valeur intermédiaire est conservée dans un registre étendu de 80 bits. Et le plus proche 80 bits valeur de précision étendue est:

 
61.99999 99999 99999 55591 07901 49937 38383 05473 32763 67187 5 

Puisque la valeur est inférieure à 62, 61 Trunc rendements depuis Trunc tours vers zéro.

Si vous avez enregistré d*100 dans une valeur double, le résultat est différent.

d := 0.62; 
d := d*100; 
i := Trunc(d); 
Writeln(i); 

Ce programme génère 62 plutôt que 61. En effet, bien que d*100 à étendre 80 précision de bits est inférieur à 62, le closest double precision value to that 80 bit value est en fait 62.

De même, si vous compilez votre programme original avec le Compilateur 64 bits, puis l'arithmétique est effectuée dans l'unité SSE qui n'a pas de registres 80 bits. Ou alors, il n'y a pas de valeur intermédiaire 80 bits et votre programme sort 62.

Ou, en remontant au compilateur 32 bits, vous pouvez organiser que les valeurs intermédiaires sont stockées à 64 bits de précision sur la FPU et également atteindre une sortie de 62. Appelez Set8087CW($1232) pour y parvenir.

Comme vous pouvez le voir, l'arithmétique binaire en virgule flottante peut parfois surprendre.


Si vous utilisez Round plutôt que Trunc la valeur retournée sera l'entier le plus proche, au lieu d'arrondir vers zéro comme Trunc fait.

Mais peut-être une meilleure solution serait d'utiliser un type de données décimal plutôt qu'un type de données binaire. Si vous faites cela, vous pouvez représenter exactement 0,62 et ainsi éviter tous ces problèmes. Le type de données décimal à valeur décimale intégré de Delphi est Currency.

+0

+1 pour être précis. –

4

Utilisation round au lieu de trunc.

round va arrondir vers l'entier le plus proche, et 62.00 est très proche de 62, il n'y a donc pas de problème. trunc arrondiront à l'entier le plus proche vers zéro, et 62.00 est très proche de 61.9999999 ainsi 'fuzz' numérique pourrait très bien causer le problème que vous décrivez.