34 votes

Comment l'ordinateur fait l'arithmétique en virgule flottante?

J'ai vu de longs articles expliquer comment stocker des nombres en virgule flottante et comment l'arithmétique de ces nombres est effectuée, mais veuillez expliquer brièvement pourquoi lorsque j'écris

 cout << 1.0 / 3.0 <<endl;
 

Je vois 0.333333 , mais quand j'écris

 cout << 1.0 / 3.0 + 1.0 / 3.0 + 1.0 / 3.0 << endl;
 

Je vois 1 .

Comment l'ordinateur fait-il cela? S'il vous plaît expliquer juste cet exemple simple. Ça me suffit.

28voto

Prasoon Saurav Points 47488

17voto

Marc Mutz - mmutz Points 10367

Nous allons faire le calcul. Par souci de concision, nous supposons que vous avez seulement quatre significative (en base 2) chiffres.

Bien sûr, depuis gcd(2,3)=1, 1/3 est périodique en cas de représentation en base 2. En particulier, il ne peut pas être représenté exactement, de sorte que nous devons nous contenter de l'approximation

A := 1×1/4 + 0×1/8 + 1×1/16 + 1*1/32

qui est plus proche de la valeur réelle de l' 1/3 de

A' := 1×1/4 + 0×1/8 + 1×1/16 + 0×1/32

Ainsi, imprimer A en décimal donne 0.34375 (le fait que vous voyez 0.33333 dans votre exemple est juste testament pour le plus grand nombre de chiffres significatifs dans un double).

Lors de l'ajout de ces trois fois, nous obtenons

A + A + A
= ( A + A ) + A
= ( (1/4 + 1/16 + 1/32) + (1/4 + 1/16 + 1/32) ) + (1/4 + 1/16 + 1/32)
= (   1/4 + 1/4 + 1/16 + 1/16 + 1/32 + 1/32   ) + (1/4 + 1/16 + 1/32)
= (      1/2    +     1/8         + 1/16      ) + (1/4 + 1/16 + 1/32)
=        1/2 + 1/4 +  1/8 + 1/16  + 1/16 + O(1/32)

L' O(1/32) terme ne peut pas être représenté dans le résultat, il est donc jetés et nous obtenons

A + A + A = 1/2 + 1/4 + 1/8 + 1/16 + 1/16 = 1

CQFD :)

17voto

DigitalRoss Points 80400

Le problème est que le format à virgule flottante représente les fractions en base 2.

La première fraction est peu½, la deuxième¼, et il s'en va comme 1 / 2n.

Et le problème c' est que chaque nombre rationnel (un nombre qui peut être exprimé comme le rapport de deux nombres entiers), en réalité, a une durée de représentation dans cette base de 2 format.

(Cela rend le format à virgule flottante difficiles à utiliser pour les valeurs monétaires. Bien que ces valeurs sont toujours des nombres rationnels (n/100) .00, .25, .50, et .75 fait exacte des représentations en quelques chiffres d'une base de deux fractions. )

De toute façon, lorsque vous ajoutez de nouveau, le système obtient finalement une chance pour arrondir le résultat à un certain nombre que peut représenter exactement.

À un certain point, il se trouve que l'ajout de la .666... numéro de la .333... l'un, comme suit:

  00111110 1  .o10101010 10101010 10101011
+ 00111111 0  .10101010 10101010 10101011o
------------------------------------------
  00111111 1 (1).0000000 00000000 0000000x  # the x isn't in the final result

Le bit plus à gauche est le signe, les huit sont l'exposant, et les bits restants sont de la fraction. Entre l'exposant et la fraction est un assummed "1" qui est toujours présent, et n'est donc pas réellement stockées, comme le normalisée la plus à gauche de la fraction bits. J'ai écrit des zéros qui ne sont pas effectivement présents en tant que personne de bits o.

Est passé beaucoup de choses ici, à chaque étape, de la FPU a pris plutôt héroïque des mesures pour arrondir le résultat. Deux chiffres supplémentaires de précision (au-delà de ce que dans la suite) ont été conservés, et le FPU sait que dans de nombreux cas, le cas échéant, ou au moins 1, de la le reste des bits les plus à droite sont un. Si oui, alors qu'une partie de la fraction est supérieure à 0,5 (à l'échelle) et donc, il arrondit. L'intermédiaire en valeurs arrondies permettent à la FPU pour réaliser la droite peu tout le chemin à la partie entière et enfin la tour de la réponse correcte.

Cela n'arrive pas parce que quelqu'un a ajouté 0,5; le FPU juste fait du mieux qu'il pouvait, dans les limites du format. Virgule flottante n'est pas, en fait, inexactes. C'est parfaitement exact, mais pas tous les numéros, nous nous attendons à voir dans notre base 10, rationnels vision du monde sont représentable par la base 2 fraction du format.

2voto

leftaroundabout Points 23679

Comme pour cet exemple précis: je pense que les compilateurs sont trop intelligents de nos jours, et automatiquement, assurez-vous que const résultat de types primitifs sera exacte, si possible. Je n'ai pas réussi à tromper g++ en faisant un simple calcul, comme c'est mal.

Cependant, il est facile de contourner de telles choses à l'aide de non-const variables. Encore,

int d = 3;
float a = 1./d;
std::cout << d*a;

exactement rendement de 1, bien que cela ne devrait pas vraiment être prévu. La raison, comme on l'a déjà dit, c'est que l' operator<< des tours de l'erreur de distance.

Comme quoi il peut faire ceci: lorsque vous ajoutez des numéros de taille similaire ou multiplier un float par int, vous obtenez à peu près tous les précision le type float peut au maximum de vous offrir - à-dire, le rapport d'erreur/le résultat est très petite (en d'autres termes, les erreurs se produisent en fin de décimale, en supposant que vous avez une bonne erreur).

Donc, 3*(1./3), même si, comme un float, pas exactement ==1, a un gros corriger les biais qui empêche operator<< de prendre soin pour les petites erreurs. Toutefois, si vous supprimez ce biais simplement en soustrayant 1, la virgule flottante va glisser vers le bas le droit à l'erreur, et tout à coup, il n'est pas négligeable du tout, pas plus. Comme je l'ai dit, cela ne se produit pas si vous tapez juste 3*(1./3)-1 parce que le compilateur est trop intelligent, mais essayez

int d = 3;
float a = 1./d;
std::cout << d*a << " - 1 = " <<  d*a - 1 << " ???\n";

Ce que je reçois (g++ 32 bits de Linux) est

1 - 1 = 2.98023e-08 ???

0voto

starblue Points 29696

Cela fonctionne car la précision par défaut est 6 chiffres et le résultat, arrondi à 6 chiffres. Voir 27.5.4.1 Constructeurs basic_ios dans le projet de norme C ++ (n3092) .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X