Question

Considérez le code suivant:

0.1 + 0.2 == 0.3  ->  false
0.1 + 0.2         ->  0.30000000000000004

Pourquoi ces inexactitudes se produisent?

Était-ce utile?

La solution

virgule flottante maths est comme ça. Dans la plupart des langages de programmation, il est basé sur le standard. JavaScript utilise une représentation en virgule flottante 64 bits, ce qui est le même que celui double de Java. Le nœud du problème est que les nombres sont représentés dans ce format comme un nombre entier de fois une puissance de deux; nombres rationnels (tels que 0.1, qui est 1/10) dont le dénominateur est pas une puissance de deux ne peuvent pas être exactement représentés.

Pour 0.1 dans le format standard binary64, la représentation peut être écrit exactement comme

En revanche, le nombre rationnel 0.1, qui est 1/10, peut être écrit exactement comme

  • 0.1 en décimal ou
  • 0x1.99999999999999...p-4 en un analogue de C99 notation hexfloat, où le ... représente une séquence sans fin de 9 années.

Les constantes 0.2 et 0.3 dans votre programme seront également des approximations à leurs vraies valeurs. Il arrive que le plus proche de double 0.2 est plus grand que le nombre rationnel 0.2 mais que le plus proche double à 0.3 est plus petit que le nombre rationnel 0.3. La somme des 0.1 et 0.2 vents par être plus grand que le nombre rationnel 0.3 et donc en désaccord avec la constante dans votre code.

Un traitement assez complet des problèmes arithmétiques à virgule flottante est Ce que tout informaticien doit savoir sur arithmétique en virgule flottante . Pour une explication plus facile à digérer, voir floating-point-gui.de .

Side Note: Tous les systèmes numériques de position (base N) partagent ce problème avec une précision

Les anciens numéros décimaux (base 10) plaine ont les mêmes problèmes, ce qui explique pourquoi le nombre comme un tiers finissent comme 0,333333333 ...

Vous venez trébuché sur un certain nombre (3/10) qui se trouve être facile à représenter avec le système décimal, mais ne correspond pas au système binaire. Il va dans les deux sens (à un faible degré) ainsi: 1/16 est un nombre laid en décimal (0,0625), mais en binaire il semble aussi propre comme 10.000ème fait en décimal (0,0001) ** - si nous étions dans l'habitude d'utiliser un système numérique de base 2 dans notre vie quotidienne, vous avait même regarder ce nombre et comprennent instinctivement, vous pouvez y arriver en réduisant de moitié quelque chose, divisé par deux fois, et encore et encore.

** Bien sûr, ce n'est pas exactement comment les nombres à virgule flottante sont stockés dans la mémoire (ils utilisent une forme de notation scientifique). Cependant, elle illustre le fait que les erreurs de précision de virgule flottante binaire ont tendance à rogner parce que les chiffres que nous sont généralement intéressés du « monde réel » à travailler avec sont si souvent puissances de dix - mais seulement parce que nous utilisons un jour- système de nombre décimal aujourd'hui. C'est aussi la raison pour laquelle nous disons des choses comme 71% au lieu de « 5 sur tous les 7 » (71% est une approximation, puisque 5/7 ne peut pas être représenté exactement avec un nombre décimal).

Donc pas: les nombres à virgule flottante de binaires ne sont pas brisés, ils ne se trouvent aussi imparfaite que tous les autres systèmes de numération en base-N:)

Side Note: Travailler avec Flotteurs en programmation

Dans la pratique, ce problème de précision signifie que vous devez utiliser les fonctions d'arrondi pour arrondir votre flotteuring numéros de points vous êtes hors cependant de décimales intéressé avant de les afficher.

Vous devez également remplacer les tests d'égalité des comparaisons qui permettent une certaine quantité de tolérance, ce qui signifie:

Do pas faire if (float1 == float2) { ... }

faire au lieu if (Math.Abs(float1 - float2) < myToleranceValue) { ... }.

myToleranceValue doit être choisi pour votre application particulière - et il aura beaucoup à voir avec la « marge de manœuvre » vous êtes prêt à permettre, et ce le plus grand nombre que vous allez comparera peut être (en raison de perte de problèmes de précision). Méfiez-vous des constantes de type « double.Epsilon » dans la langue de votre choix (Number.EPSILON en Javascript). Ceux-ci sont pas à utiliser comme des valeurs de tolérance.

Plus d'infos sur Tolerances:

(auto-promotion éhontée par un éditeur - désolé pour le hijack)

Je l'ai mis en place une explication plus détaillée de la façon de choisir une tolérance, et pourquoi éviter Number.EPSILON et son acabit à https://dev.to/alldanielscott/how-to-compare-numbers-correctly-in-javascript-1l4i

Autres conseils

Perspective

Hardware Designer est un

Je crois que je devrais ajouter la perspective d'un concepteur de matériel à ce que je conception et la construction du matériel à virgule flottante. Connaissant l'origine de l'erreur peut aider à comprendre ce qui se passe dans le logiciel, et en fin de compte, j'espère que cela aide à expliquer les raisons pour lesquelles les erreurs de virgule flottante se produisent et semblent accumuler au fil du temps.

1. Vue d'ensemble

Du point de vue de l'ingénierie, la plupart des opérations en virgule flottante aura un élément d'erreur puisque le matériel qui effectue les calculs en virgule flottante est uniquement nécessaire d'avoir une erreur de moins d'une moitié d'une unité à la dernière place. Par conséquent, beaucoup de matériel s'arrête à une précision qui est seulement nécessaire pour produire une erreur de moins d'une moitié d'une unité à la dernière place pour une seule opération qui est particulièrement problématique dans la division à virgule flottante. Ce qui constitue une seule opération dépend du nombre opérandes l'unité prend. Pour la plupart, il est deux, mais certaines unités prennent 3 opérandes ou plus. À cause de cela, il n'y a aucune garantie que les opérations répétées entraîneront une erreur souhaitable, car les erreurs s'additionnent au fil du temps.

2. Normes

La plupart des processeurs suivent le IEEE-754 standard, mais une utilisation dénormalisées ou différentes normes . Par exemple, il existe un mode dénormalisé dans la norme IEEE-754 qui permet la représentation des nombres à virgule flottante très faible au détriment de la précision. Ce qui suit, cependant, couvrira le mode normalisé de la norme IEEE-754 qui est le mode de fonctionnement typique.

Dans la norme IEEE-754, les concepteurs de matériel sont autorisés une valeur d'erreur / epsilon tant qu'il est inférieur à la moitié d'une unité à la dernière place, et le résultat ne doit être inférieure à la moitié d'une unité à la dernière place pour une seule opération. Cela explique pourquoi quand il y a des opérations répétées, les erreurs s'additionnent. Pour IEEE-754 double précision, ceci est le 54ème bit, étant donné que 53 bits sont utilisés pour représenter la partie numérique (normalisés), également appelé la mantisse, du nombre à virgule flottante (par exemple le 5,3 en 5.3e5). Les sections suivantes vont plus en détail sur les causes de l'erreur matérielle sur diverses opérations en virgule flottante.

3. Cause de l'erreur dans la division Arrondi

La principale cause de l'erreur de division à virgule flottante est l'algorithme de division utilisés pour calculer le quotient. La plupart des systèmes informatiques calculent la division en utilisant la multiplication par l'inverse, principalement dans Z=X/Y, Z = X * (1/Y). Une division est calculée de façon itérative à-dire chaque cycle calcule des bits du quotient jusqu'à ce que la précision souhaitée soit atteinte, ce qui pour IEEE-754 est tout avec une erreur de moins d'une unité dans la dernière place. Le tableau des inverses des Y (1 / Y) est connu sous le nom de la table de sélection de quotient (QST) dans la division lente, et la taille en bits de la table de sélection de quotient est habituellement la largeur de la base, ou d'un nombre de bits de le quotient calculé à chaque itération, ainsi que quelques bits de garde. Pour le standard IEEE-754, double précision (64 bits), il serait la taille de la base du diviseur, ainsi que quelques bits de garde k, où k>=2. Ainsi, par exemple, un tableau typique Quotient de sélection d'un diviseur qui calcule 2 bits du quotient à la fois (radix 4) serait de bits 2+2= 4 (plus de quelques bits en option).

3.1 Division Arrondi Erreur: rapprochement des réciproque

Dans les inverses sont dans la table de sélection de quotient dépend de la méthode de : division lente comme la division SRT, ou division rapide telles que la division Goldschmidt; chaque entrée est modifiée en fonction de l'algorithme de division dans une tentative pour obtenir la plus faible erreur possible. Dans tous les cas, cependant, tous les ar inversese approximations de l'inverse et d'introduire un élément d'erreur réelle. Les deux division lente et des procédés de division rapide de calculer le quotient de manière itérative, à savoir un certain nombre de bits du quotient est calculé à chaque étape, le résultat est soustrait du dividende et le diviseur répète les étapes jusqu'à ce que l'erreur est inférieure à une moitié d'une unité à la dernière place. méthodes de division lente calculent un nombre fixe de chiffres du quotient à chaque étape et sont généralement moins coûteux à construire, et les méthodes de division rapide calculent un nombre variable de chiffres par étape et sont généralement plus coûteux à construire. La partie la plus importante des méthodes de division est que la plupart d'entre eux comptent sur la multiplication répétée par une approximation d'une réciproque, ils sont sujets à l'erreur.

4. Erreurs dans d'autres arrondissage opérations: troncature

Une autre cause des erreurs d'arrondi dans toutes les opérations sont les différents modes de troncature de la réponse finale qui permet IEEE-754. Il y a tronquer, rond-vers zéro, , rond -down et round-up. Toutes les méthodes introduisent un élément d'erreur de moins d'une unité à la dernière place pour une seule opération. Au fil du temps et des opérations répétées, troncature ajoute également de façon cumulative à l'erreur résultante. Cette erreur de troncature est particulièrement problématique dans exponentiation, ce qui implique une certaine forme de multiplication répétée.

5. Opérations répétées

Étant donné que le matériel qui effectue les calculs en virgule flottante n'a besoin que pour donner un résultat avec une erreur de moins d'une moitié d'une unité à la dernière place pour une seule opération, l'erreur va croître au cours des opérations répétées sinon surveillés. Ceci est la raison pour laquelle dans les calculs qui nécessitent une erreur limitée, les mathématiciens utilisent des méthodes telles que l'utilisation de la ronde à la plus proche arithmétique Intervalle combiné avec des variations de la IEEE 754 modes d'arrondi pour prédire les erreurs d'arrondi, et de les corriger. En raison de son erreur faible par rapport à d'autres modes d'arrondi, arrondi au plus proche même chiffre (en dernier lieu), est le mode d'arrondi par défaut de la norme IEEE-754.

Notez que le mode d'arrondi par défaut, rond à la plus proche même chiffre à la dernière place , garantit une erreur inférieure à une moitié d'une unité dans la dernière place pour une opération. Utilisation de la troncature, tour d'horizon, et arrondir seule peut entraîner une erreur qui est supérieure à la moitié d'une unité à la dernière place, mais moins d'une unité à la dernière place, de sorte que ces modes ne sont pas recommandés à moins qu'ils ne sont utilisé dans l'intervalle arithmétique.

6. Résumé

En bref, la raison fondamentale pour les erreurs dans opérations en virgule flottante est une combinaison de la troncature dans le matériel et la troncature d'une réciprocité dans le cas de la division. Étant donné que la norme IEEE-754 nécessite seulement une erreur de moins d'une moitié d'une unité à la dernière place pour une seule opération, ajoutera à moins que corrigé les erreurs à virgule flottante sur les opérations répétées.

Lorsque vous convertissez .1 ou 1/10 à la base 2 (binaire), vous obtenez un motif répétitif après la virgule, comme essayer de représenter un tiers dans la base 10. La valeur est pas exacte, et donc vous pouvez « t faire des mathématiques exacte avec elle en utilisant des méthodes normales de virgule flottante.

La plupart des réponses ici abordent cette question en termes très secs, techniques. Je voudrais aborder cela en termes que les êtres humains normaux peuvent comprendre.

Imaginez que vous essayez de découper les pizzas. Vous avez un couteau à pizza robot qui peut couper des tranches de pizza exactement dans la moitié. Il peut réduire de moitié une pizza entière, ou il peut réduire de moitié une tranche existante, mais en tout cas, la réduction de moitié est toujours exacte.

Ce couteau à pizza a de très beaux mouvements, et si vous commencez avec une pizza entière, puis réduire de moitié que, et continuer de réduire de moitié la plus petite tranche chaque fois, vous pouvez faire la réduction de moitié 53 fois avant la tranche est trop petit même pour ses capacités de haute précision. À ce moment-là, vous ne pouvez plus réduire de moitié cette tranche très mince, mais doit inclure ou exclure tout comme.

Maintenant, comment voulez-vous toutes les tranches morceaux d'telle manière qui ajouterait à un dixième (0,1) ou un cinquième (0,2) d'une pizza? Pensez vraiment, et essayer de travailler dehors. Vous pouvez même essayer d'utiliser une vraie pizza, si vous avez un coupe mythique pizza de précision à portée de main. : -)


programmeurs les plus expérimentés, bien sûr, connaître la vraie réponse, ce qui est qu'il n'y a aucun moyen de rassembler une exactement dixième ou cinquième de la pizza en utilisant ces tranches, peu importe la façon dont finement vous tranche leur. Vous pouvez faire une assez bonne approximation, et si vous ajoutez l'approximation de 0,1 avec l'approximation de 0,2, vous obtenez une très bonne approximation de 0,3, mais il est encore juste que, une approximation.

Pour les nombres double précision (qui est la précision qui vous permet de réduire de moitié votre pizza 53 fois), le nombre immédiatement inférieur et supérieur à 0,1 sont 0,09999999999999999167332731531132594682276248931884765625 et 0,1000000000000000055511151231257827021181583404541015625. Ce dernier est un peu plus proche de 0,1 que l'ancien, donc un analyseur numérique sera donné une entrée de 0,1, favoriser celle-ci.

(La différence entre ces deux nombres est la « plus petite tranche » que nous devons décider soit de comprendre, qui introduit un biais vers le haut, ou exclure, ce qui introduit un biais à la baisse. Le terme technique pour que la plus petite tranche est un ULP .)

Dans le cas de 0,2, les chiffres sont tous les mêmes, juste mis à l'échelle par un facteur de 2. Encore une fois, nous privilégions la valeur qui est légèrement supérieur à 0,2.

Notez que dans les deux cas, les approximations de 0,1 et 0,2 ont un léger biais vers le haut. Si l'on ajoute assez de ces biais, ils pousseront le nombre de plus en plus loin de ce que nous voulons, et en fait, dans le cas de 0,1 + 0,2, le biais est suffisamment élevé pour que le nombre résultant ne soit plus le nombre le plus proche à 0,3.

En particulier, 0,1 + 0,2 + est vraiment 0.1000000000000000055511151231257827021181583404541015625 0.200000000000000011102230246251565404236316680908203125 = 0.3000000000000000444089209850062616169452667236328125, tandis que le nombre le plus proche de 0,3 est en fait 0,299999999999999988897769753748434595763683319091796875.


P.S. Certains langages de programmation fournissent également des coupe-pizza qui peut diviser les tranches en dixièmes exacte. Bien que ces coupeurs de pizza sont rares, si vous avez accès à l'un, vous devez l'utiliser quand il est important d'être en mesure d'obtenir exactement un dixième ou un cinquième d'une tranche.

(Initialement posté sur Quora.)

erreurs d'arrondi à virgule flottante. 0,1 ne peut pas représenter le plus fidèlement à base 2 comme base 10 en raison du facteur premier manquant de 5. Tout comme tiers prend un nombre infini de décimales dans décimale, mais est « 0,1 » dans la base 3, 0,1 prend un nombre infini de chiffres en base 2 où il ne constitue pas en base 10. Et les ordinateurs ne sont pas une quantité infinie de mémoire.

En plus des autres réponses, vous pouvez envisager de réduire vos valeurs pour éviter des problèmes avec l'arithmétique à virgule flottante.

Par exemple:

var result = 1.0 + 2.0;     // result === 3.0 returns true

... au lieu de:

var result = 0.1 + 0.2;     // result === 0.3 returns false

Le 0.1 + 0.2 === 0.3 d'expression retourne false en JavaScript, mais heureusement l'arithmétique entière en virgule flottante est exacte, alors les erreurs de représentation décimale peut être évité en mise à l'échelle.

A titre d'exemple pratique, pour éviter les problèmes à virgule flottante où la précision est primordiale, il est recommandé 1 pour gérer l'argent comme un entier représentant le nombre de cents: cents 2550 au lieu de dollars 25.50.


1 Douglas Crockford: JavaScript: The Good Parts : Annexe a - Pièces Awful (page 105) .

Ma réponse est assez longue, donc j'ai divisé en trois sections. Étant donné que la question concerne les mathématiques à virgule flottante, j'ai mis l'accent sur ce que la machine ne fait. Je l'ai aussi fait-il spécifique au double (64 bits) de précision, mais l'argument vaut également pour les opérations arithmétiques à virgule flottante.

Préambule

IEEE 754 double précision binaire format à virgule flottante (binary64) nombre représente un nombre de la forme

  

value = (-1) s ^ * (1.m 51 m 50 ... m 2 m 1 m 0 ) 2 * 2 e-1023

en 64 bits:

1 - IEEE 754 permet le concept d'un signé zéro - +0 et -0 sont traités différemment: 1 / (+0) est infini positif; 1 / (-0) est infini négatif. Pour les valeurs zéro, les bits de mantisse et exposant sont tous nuls. Remarque:. Valeurs zéro (+0 et -0) sont explicitement pas classés comme denormal 2

2 - Ce n'est pas le cas pour numéros denormal , qui présentent un exposant de décalage de zéro (et un 0. implicite). La gamme des nombres dénormalisés double précision est d min ≤ | x | ≤ d max , où d min (le nombre non nul représentable le plus faible) est de 2 -1023 à 51 (≈ 4,94 * 10 - 324 ) et d max (le plus grand nombre dénormalisé, dont la mantisse est entièrement constitué de 1s) est de 2 -1023 + 1 - 2 - 1023 -. 51 (≈ 2,225 * 10 -308 )


Transformer un nombre double précision binaire

De nombreux convertisseurs en ligne existent pour convertir un nombre double précision en virgule flottante en binaire (par exemple binaryconvert.com ), mais voici quelques exemples de code C # pour obtenir la représentation IEEE 754 pour un nombre double de précision (je sépare les trois parties avec (:) côlons:

public static string BinaryRepresentation(double value)
{
    long valueInLongType = BitConverter.DoubleToInt64Bits(value);
    string bits = Convert.ToString(valueInLongType, 2);
    string leadingZeros = new string('0', 64 - bits.Length);
    string binaryRepresentation = leadingZeros + bits;

    string sign = binaryRepresentation[0].ToString();
    string exponent = binaryRepresentation.Substring(1, 11);
    string mantissa = binaryRepresentation.Substring(12);

    return string.Format("{0}:{1}:{2}", sign, exponent, mantissa);
}

Obtenir au point: la question initiale

(Passer au fond de la TL, la version DR)

Cato Johnston (le demandeur question) a demandé pourquoi 0,1 + 0,2! = 0,3.

écrit en binaire (avec deux points séparant les trois parties), l'IEEE 754 représentations des valeurs sont:

0.1 => 0:01111111011:1001100110011001100110011001100110011001100110011010
0.2 => 0:01111111100:1001100110011001100110011001100110011001100110011010

Notez que la mantisse se compose de chiffres de 0011 récurrents. Ceci est touche les raisons pour lesquelles il y a une erreur dans les calculs - 0,1, 0,2 et 0,3 ne peut pas être représenté en binaire précisément dans plusieurs fini de bits binaires, pas plus que 1/9, 1/3 ou 7/1 peuvent être représentées avec précision dans chiffres décimaux .

Notez également que nous pouvons diminuer la puissance de l'exposant par 52 et décaler le point dans la représentation binaire à droite de 52 places (un peu comme 10 -3 * 1,23 == 10 -5 * 123). Cela nous permet ensuite de représenter la représentation binaire comme la valeur exacte qu'elle représente sous la forme d'une * 2 p . où 'a' est un nombre entier.

Conversion des exposants en décimal, en supprimant le décalage, et re-adjonction 1 implicite (entre crochets), 0,1 et 0,2 sont:

0.1 => 2^-4 * [1].1001100110011001100110011001100110011001100110011010
0.2 => 2^-3 * [1].1001100110011001100110011001100110011001100110011010
or
0.1 => 2^-56 * 7205759403792794 = 0.1000000000000000055511151231257827021181583404541015625
0.2 => 2^-55 * 7205759403792794 = 0.200000000000000011102230246251565404236316680908203125

Pour ajouter deux nombres, l'exposant doit être le même, i.e.:.

0.1 => 2^-3 *  0.1100110011001100110011001100110011001100110011001101(0)
0.2 => 2^-3 *  1.1001100110011001100110011001100110011001100110011010
sum =  2^-3 * 10.0110011001100110011001100110011001100110011001100111
or
0.1 => 2^-55 * 3602879701896397  = 0.1000000000000000055511151231257827021181583404541015625
0.2 => 2^-55 * 7205759403792794  = 0.200000000000000011102230246251565404236316680908203125
sum =  2^-55 * 10808639105689191 = 0.3000000000000000166533453693773481063544750213623046875

Étant donné que la somme n'est pas de la forme 2 n * 1. {} bbb nous augmentons l'exposant par un et décalent la décimale ( binaire ) le point d'obtenir:

sum = 2^-2  * 1.0011001100110011001100110011001100110011001100110011(1)
    = 2^-54 * 5404319552844595.5 = 0.3000000000000000166533453693773481063544750213623046875

Il y a maintenant 53 bits dans la mantisse (53e est entre crochets dans la ligne ci-dessus). La valeur par défaut mode d'arrondi IEEE 754 est « arrondi au plus proche - à savoir si un nombre x se situe entre deux valeurs a et b , la valeur où le bit le moins significatif est nul est choisie.

a = 2^-54 * 5404319552844595 = 0.299999999999999988897769753748434595763683319091796875
  = 2^-2  * 1.0011001100110011001100110011001100110011001100110011

x = 2^-2  * 1.0011001100110011001100110011001100110011001100110011(1)

b = 2^-2  * 1.0011001100110011001100110011001100110011001100110100
  = 2^-54 * 5404319552844596 = 0.3000000000000000444089209850062616169452667236328125

Notez que a et b ne diffèrent que dans le dernier bit; ...0011 + 1 = ...0100. Dans ce cas, la valeur avec le bit le moins significatif de zéro est b , de sorte que la somme est:

sum = 2^-2  * 1.0011001100110011001100110011001100110011001100110100
    = 2^-54 * 5404319552844596 = 0.3000000000000000444089209850062616169452667236328125

alors que la représentation binaire de 0,3 est la suivante:

0.3 => 2^-2  * 1.0011001100110011001100110011001100110011001100110011
    =  2^-54 * 5404319552844595 = 0.299999999999999988897769753748434595763683319091796875

qui ne diffère de la représentation binaire de la somme de 0,1 et 0,2 par 2 -54 .

La représentation binaire de 0,1 et 0,2 sont les plus précis représentations des numéros autorisés par la norme IEEE 754. L'ajout de ces représentation, en raison du mode d'arrondi par défaut, se traduit par une valeur qui ne diffère que dans le moins significatif bits.

TL; DR

0.1 + 0.2 d'écriture dans une représentation binaire IEEE 754 (avec deux points séparant les trois parties) et en le comparant à 0.3, ceci est (I ai mis les bits distincts entre crochets):

0.1 + 0.2 => 0:01111111101:0011001100110011001100110011001100110011001100110[100]
0.3       => 0:01111111101:0011001100110011001100110011001100110011001100110[011]

reconverti en décimal, ces valeurs sont:

0.1 + 0.2 => 0.300000000000000044408920985006...
0.3       => 0.299999999999999988897769753748...

La différence est exactement 2 -54 , qui est ~ 5,5511151231258 × 10 -17 -. Insignifiante (pour de nombreuses applications) par rapport aux valeurs initiales

En comparant les derniers bits d'un nombre à virgule flottante est intrinsèquement dangereux, comme tous ceux qui lisent le fameux « ce que tout informaticien doit savoir sur arithmétique à virgule flottante » (qui couvre toutes les grandes parties de cette réponse) saura.

La plupart des calculatrices utilisent pour contourner ce problème, ce qui est de savoir comment 0.1 + 0.2 donnerait 0.3:. les bits quelques derniers sont arrondis

nombres à virgule flottante stockés dans l'ordinateur sont constituées de deux parties, un nombre entier et un exposant que la base est prise et multipliée par la partie entière.

Si l'ordinateur travaillaient dans la base 10, 0.1 serait 1 x 10⁻¹, 0.2 serait 2 x 10⁻¹ et 0.3 serait 3 x 10⁻¹. maths entier est facile et exacte, afin d'ajouter 0.1 + 0.2 entraînera évidemment 0.3.

Les ordinateurs ne fonctionnent généralement pas dans la base 10, ils travaillent dans la base 2. Vous pouvez toujours obtenir des résultats exacts pour certaines valeurs, par exemple 0.5 est 1 x 2⁻¹ et 0.25 est 1 x 2⁻², et en les ajoutant à des résultats 3 x 2⁻² ou 0.75. Exactement.

Le problème vient avec des chiffres qui peuvent être représentés exactement dans la base 10, mais pas dans la base 2. Ces chiffres doivent être arrondis à leur plus proche équivalent. Assumant le format commun à virgule flottante IEEE 64 bits, le nombre le plus proche de 0.1 est 3602879701896397 x 2⁻⁵⁵, et le nombre le plus proche de 0.2 est 7205759403792794 x 2⁻⁵⁵; les additionner les résultats dans 10808639105689191 x 2⁻⁵⁵, ou une valeur décimale exacte de 0.3000000000000000444089209850062616169452667236328125. nombres à virgule flottante sont généralement arrondis pour l'affichage.

erreur d'arrondi en virgule flottante. De Ce que tout informaticien doit savoir sur Arithmétique à virgule flottante :

  

Serrant une infinité de nombres réels en un nombre fini de bits nécessite une représentation approximative. Bien qu'il existe une infinité d'entiers, dans la plupart des programmes le résultat des calculs entiers peuvent être stockés en 32 bits. En revanche, étant donné un nombre fixe de bits, la plupart des calculs avec des nombres réels produisent des quantités qui ne peuvent pas être exactement représentés en utilisant autant de bits. Par conséquent, le résultat d'un calcul à virgule flottante doit souvent être arrondie afin de se réinsérer dans sa représentation finie. Cette erreur d'arrondi est le trait caractéristique de calcul à virgule flottante.

Ma solution:

function add(a, b, precision) {
    var x = Math.pow(10, precision || 2);
    return (Math.round(a * x) + Math.round(b * x)) / x;
}

précision fait référence au nombre de chiffres que vous souhaitez conserver après la virgule lors de l'addition.

Beaucoup de bonnes réponses ont été affichées, mais j'aimerais ajouter un autre.

Tous les chiffres peuvent être représentés par flotte / Double Par exemple, le nombre « 0,2 » sera représenté comme « 0,200000003 » en simple précision en standard point flottant IEEE754.

Modèle pour les nombres réels de magasin sous le capot représentent les numéros de flotteur comme

 ici

Même si vous pouvez taper 0.2 facilement, FLT_RADIX et DBL_RADIX est 2; pas 10 pour un ordinateur avec FPU qui utilise "la norme IEEE pour arithmétique binaire à virgule flottante (ISO / IEEE Std 754-1985)".

Il est donc un peu difficile de représenter ces chiffres exactement. Même si vous spécifiez explicitement cette variable sans calcul intermédiaire.

Quelques statistiques liées à cette fameuse double question de précision.

Lorsque vous ajoutez toutes les valeurs ( a + b ) en utilisant une étape de 0,1 (de 0,1 à 100) nous avons ~ 15% de chances d'erreur de précision . Notez que l'erreur pourrait entraîner des valeurs légèrement plus grandes ou plus petites. Voici quelques exemples:

0.1 + 0.2 = 0.30000000000000004 (BIGGER)
0.1 + 0.7 = 0.7999999999999999 (SMALLER)
...
1.7 + 1.9 = 3.5999999999999996 (SMALLER)
1.7 + 2.2 = 3.9000000000000004 (BIGGER)
...
3.2 + 3.6 = 6.800000000000001 (BIGGER)
3.2 + 4.4 = 7.6000000000000005 (BIGGER)

Lorsque la soustraction de toutes les valeurs ( a - b a> b ) en utilisant une étape de 0,1 (de 100 à 0,1), nous avons ~ 34% de chances d'erreur de précision . Voici quelques exemples:

0.6 - 0.2 = 0.39999999999999997 (SMALLER)
0.5 - 0.4 = 0.09999999999999998 (SMALLER)
...
2.1 - 0.2 = 1.9000000000000001 (BIGGER)
2.0 - 1.9 = 0.10000000000000009 (BIGGER)
...
100 - 99.9 = 0.09999999999999432 (SMALLER)
100 - 99.8 = 0.20000000000000284 (BIGGER)

* 15% et 34% sont en effet énorme, donc toujours utiliser BigDecimal lorsque la précision est de grande importance. Avec 2 décimales (étape 0.01), la situation se dégrade un peu plus (18% et 36%).

Non, pas cassé, mais la plupart des fractions décimales doivent être approchées

  

Résumé

arithmétique en virgule flottante est exact, malheureusement, il ne correspond pas bien avec notre représentation habituelle du numéro de base 10, donc il se trouve que nous sommes souvent lui donnons entrée qui est un peu hors de ce nous avons écrit.

Même nombres simples comme 0,01, 0,02, 0,03, 0,04 ... 0,24 ne sont pas représentables exactement sous forme de fractions binaires. Si vous comptez 0.01, .02, .03 ..., jusqu'à ce que vous obtenez à 0,25 vous obtiendrez la première fraction représentables dans la base 2 . Si vous avez essayé qu'utiliser FP, 0,01 aurait été légèrement, de sorte que la seule façon d'ajouter 25 d'entre eux jusqu'à une belle exacte 0,25, il aurait fallu une longue chaîne de causalité impliquant des bits de garde et de l'arrondissement. Il est difficile de prédire si nous jetons nos mains et dire « FP est inexact », , mais ce n'est pas vraiment vrai.

Nous donnons toujours quelque chose de matériel FP qui semble simple base 10, mais est une fraction de répétition dans la base 2.

  

Comment est-ce arrivé?

Quand nous écrivons en décimal, chaque fraction (en particulier, tous les décimale de terminaison) est un nombre rationnel de la forme

a / (2 n x 5 m )

En binaire, on obtient seulement le 2 n terme, qui est:

a / 2 n

Donc en décimal, on ne peut pas représenter 1 / 3 . Parce que la base 10 comprend 2 comme facteur premier, chaque numéro, nous pouvons écrire comme une fraction binaire aussi peut être écrit comme une fraction de base 10. Cependant, presque rien, nous écrivons comme base 10 fraction est représentable en binaire. Dans la gamme de 0,01, 0,02, 0,03 ... 0,99, seulement trois chiffres peuvent être représentés dans notre format FP: 0,25, 0,50 et 0,75, car ils sont 1/4, 1/2, et 3/4, tous les nombres avec un premier facteur en utilisant uniquement le 2 n terme .

Dans la base 10 nous ne pouvons pas représenter 1 / 3 . Mais en binaire, nous ne pouvons pas faire 1 / 10 ou 1 / 3 .

Ainsi, alors que chaque fraction binaire peut être écrit en décimal, l'inverse est pas vrai. Et en fait, la plupart des fractions décimales répéter en binaire.

  

avec elle Traiter

Les développeurs sont généralement chargés de faire comparaisons, de meilleurs conseils pourraient être à arrondir des valeurs entières (dans la bibliothèque C: round () et roundf (), à savoir, rester dans le format FP ) puis comparer. Arrondir à une longueur de fraction décimale spécifique permet de résoudre la plupart des problèmes avec la production.

En outre, sur de vrais problèmes crissement nombre (les problèmes que FP a été inventé pour le début, les ordinateurs effroyablement coûteux) les constantes physiques de l'univers et toutes les autres mesures ne sont connues que pour un nombre relativement faible de chiffres significatifs, de sorte que le tout l'espace de problème était « inexact » de toute façon. FP « précision » est pas un problème dans ce genre d'application.

Toute la question se pose vraiment quand les gens essaient d'utiliser la PF pour le comptage de haricots. Il ne fonctionne pour cela, mais seulement si vous en tenir à des valeurs entières qui défaites genre du point de l'utiliser. C'est pourquoi nous avons toutes les bibliothèques de logiciels de fraction décimale.

J'adore la réponse Pizza par Chris , car il décrit le problème réel, non seulement l'habituel A propos argument qualitatif « inexactitude ». Si FP étaient simplement « inexactes », nous pourrions fix et que l'aurions fait il y a quelques décennies. La raison pour laquelle nous n'avons pas parce que le format FP est compact et rapide et il est la meilleure façon de craquer beaucoup de chiffres. Aussi, il est un héritage de la course de l'ère spatiale et les bras et les premières tentatives pour résoudre de gros problèmes avec des ordinateurs très lents à l'aide de petits systèmes de mémoire. (Parfois, individuels noyaux magnétiques pour le stockage de 1 bit, Mais c'est une autre histoire. )

  

Conclusion

Si vous êtes comptez les haricots dans une banque, des solutions logicielles qui utilisent des représentations de chaîne décimales dans le premier travail place parfaitement. Mais vous ne pouvez pas faire chromodynamique quantique ou l'aérodynamique de cette façon.

Avez-vous essayé la solution de ruban adhésif?

Essayez de déterminer quand des erreurs se produisent et les fixer avec court si les déclarations, ce n'est pas assez, mais pour certains problèmes, il est la seule solution, ce qui est l'un d'entre eux.

 if( (n * 0.1) < 100.0 ) { return n * 0.1 - 0.000000000000001 ;}
                    else { return n * 0.1 + 0.000000000000001 ;}    

J'ai eu le même problème dans un projet de simulation scientifique c #, et je peux vous dire que si vous ignorez l'effet papillon, il est tour va à un grand dragon de graisse et de vous mordre dans le un **

Ces chiffres étranges apparaissent parce que les ordinateurs utilisent le système de numération binaire (base 2) à des fins de calcul, alors que nous utilisons décimal (base 10).

Il y a une majorité de nombres fractionnaires qui ne peut pas être représenté précisément, soit en binaire ou en décimal, ou les deux. Résultat - Un arrondi (mais précis) les résultats de nombre

.

Afin d'offrir meilleure solution Je peux dire que je découvre la méthode suivante:

parseFloat((0.1 + 0.2).toFixed(10)) => Will return 0.3

Laissez-moi vous expliquer pourquoi il est la meilleure solution. Comme d'autres mentionnés dans les réponses ci-dessus, il est une bonne idée d'utiliser prêt à utiliser la fonction Javascript toFixed () pour résoudre le problème. Mais le plus probable que vous allez rencontrer quelques problèmes.

Imaginez que vous allez ajouter deux nombres flottants comme 0.2 et 0.7 ici est:. 0.2 + 0.7 = 0.8999999999999999

Votre résultat attendu a été 0.9 cela signifie que vous avez besoin d'un résultat avec une précision de 1 chiffres dans ce cas. Donc, vous devriez avoir utilisé (0.2 + 0.7).tofixed(1) mais vous ne pouvez pas donner un certain paramètre à toFixed () car il dépend du nombre donné, par exemple

`0.22 + 0.7 = 0.9199999999999999`

Dans cet exemple, vous avez besoin de 2 chiffres précis, il devrait être toFixed(2), donc ce devrait être le paramter pour adapter chaque numéro de flotteur donné?

Vous pourriez dire que ce soit 10 dans toutes les situations alors:

(0.2 + 0.7).toFixed(10) => Result will be "0.9000000000"

Bon sang! Qu'allez-vous faire avec ces zéros indésirables après 9? Il est le temps de le convertir à flotter pour le faire que vous le désirez:

parseFloat((0.2 + 0.7).toFixed(10)) => Result will be 0.9

Maintenant que vous avez trouvé la solution, il est préférable d'offrir en fonction comme ceci:

function floatify(number){
           return parseFloat((number).toFixed(10));
        }

Essayons vous-même:

function floatify(number){
       return parseFloat((number).toFixed(10));
    }
 
function addUp(){
  var number1 = +$("#number1").val();
  var number2 = +$("#number2").val();
  var unexpectedResult = number1 + number2;
  var expectedResult = floatify(number1 + number2);
  $("#unexpectedResult").text(unexpectedResult);
  $("#expectedResult").text(expectedResult);
}
addUp();
input{
  width: 50px;
}
#expectedResult{
color: green;
}
#unexpectedResult{
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="number1" value="0.2" onclick="addUp()" onkeyup="addUp()"/> +
<input id="number2" value="0.7" onclick="addUp()" onkeyup="addUp()"/> =
<p>Expected Result: <span id="expectedResult"></span></p>
<p>Unexpected Result: <span id="unexpectedResult"></span></p>

Vous pouvez l'utiliser de cette façon:

var x = 0.2 + 0.7;
floatify(x);  => Result: 0.9

W3Schools suggère qu'il est aussi une autre solution, vous pouvez multiplier et diviser pour résoudre le problème ci-dessus:

var x = (0.2 * 10 + 0.1 * 10) / 10;       // x will be 0.3

Gardez à l'esprit que (0.2 + 0.1) * 10 / 10 ne fonctionnera pas du tout bien qu'il semble même! Je préfère la première solution que je peux l'appliquer en fonction qui convertit le flotteur d'entrée à flotteur de sortie précise.

Étant donné que personne n'a mentionné cette ...

Certains langages de haut niveau tels que Python et Java sont des outils pour surmonter les limites binaires de virgule flottante. Par exemple:

Aucune de ces solutions est parfait (surtout si l'on regarde les performances, ou si nous avons besoin d'une très grande précision), mais ils résoudre un grand nombre de problèmes avec l'arithmétique binaire en virgule flottante.

Beaucoup de nombreux doublons de cette question poser des questions sur les effets de flottement arrondi point sur des chiffres précis. Dans la pratique, il est plus facile d'obtenir un sentiment de la façon dont cela fonctionne en regardant les résultats exacts des calculs d'intérêt plutôt que de lire à ce sujet. Certaines langues offrent des moyens de le faire -. Tels que la conversion d'un float ou double à BigDecimal en Java

Comme il est une question de langue agnostique, il a besoin d'outils linguistiques agnostique, comme un Convertisseur décimal à virgule flottante .

L'appliquer aux chiffres de la question, traités comme double:

0,1 convertis à 0,1000000000000000055511151231257827021181583404541015625,

0,2 convertis à 0,200000000000000011102230246251565404236316680908203125,

0,3 convertis à 0,299999999999999988897769753748434595763683319091796875 et

,30000000000000004 convertit à 0,3000000000000000444089209850062616169452667236328125.

Ajout les deux premiers numéros manuellement ou dans un calculateur décimal tel que pleine précision Calculatrice , montre la somme exacte des entrées réelles est 0,3000000000000000166533453693773481063544750213623046875.

Si elle était arrondi à l'équivalent de 0,3 l'erreur d'arrondi serait 0,0000000000000000277555756156289135105907917022705078125. Arrondir jusqu'à l'équivalent de 0,30000000000000004 donne également erreur d'arrondi 0,0000000000000000277555756156289135105907917022705078125. Le rond à même cravate applique disjoncteur.

De retour au convertisseur à virgule flottante, la première pour hexadécimal 0,30000000000000004 est 3fd3333333333334, qui se termine dans un même chiffre et est par conséquent le résultat correct.

Puis-je ajouter; les gens supposent toujours l'existence de ce problème informatique, mais si vous comptez avec vos mains (base 10), vous ne pouvez pas obtenir (1/3+1/3=2/3)=true sauf si vous avez l'infini d'ajouter 0,333 ... à 0,333 ... donc tout comme le problème de (1/10+2/10)!==3/10 dans la base 2, vous tronquer à 0,333 + 0,333 = 0,666 et rond probablement à 0,667 qui serait également techniquement inexact.

Décompte ternaire, et tiers ne sont pas un problème - peut-être une course avec 15 doigts sur chaque main se demander pourquoi votre mathématiques décimal a été cassé ...

Le genre de mathématiques à virgule flottante qui peut être mis en œuvre dans un ordinateur numérique utilise nécessairement une approximation des nombres réels et les opérations sur eux. (La version standard court à plus de cinquante pages de documentation et a un comité pour traiter son errata et plus de raffinement.)

Cette approximation est un mélange d'approximations de différents types, dont chacun peut soit être ignorés ou comptabilisés avec soin pour en raison de sa manière spécifique de déviation par rapport à l'exactitude. Elle implique aussi un certain nombre de cas exceptionnels explicites tant au niveau matériel et logiciel que la plupart des gens marchent droit passé tout en faisant semblant de ne pas remarquer.

Si vous avez besoin de précision infinie (en utilisant le nombre π, par exemple, au lieu d'un de ses nombreux stand-ins plus courtes), vous devriez écrire ou utiliser un programme de mathématiques symbolique à la place.

Mais si vous êtes d'accord avec l'idée qui est floue en valeur et la logique et des erreurs peuvent parfois mathématiques à virgule flottante accumuler rapidement, et vous pouvez écrire vos exigences et des tests pour permettre que, votre code peut souvent obtenir par avec ce qui est dans votre FPU.

Juste pour le plaisir, je jouais avec la représentation des flotteurs, selon les définitions de la norme C99 et j'ai écrit le code ci-dessous.

Le code imprime la représentation binaire de flotteurs en 3 groupes séparés

SIGN EXPONENT FRACTION

et après qu'il imprime une somme, que, lorsqu'on les additionne avec suffisamment de précision, il indique la valeur qui existe réellement dans le matériel.

Alors, quand vous écrivez float x = 999..., le compilateur transformera ce nombre dans une représentation binaire imprimée par la fonction xx telle que la somme imprimée par la fonction yy soit égal au nombre donné.

En réalité, cette somme ne représente qu'une approximation. Pour le numéro 999999999 le compilateur insérera dans la représentation binaire du flotteur le numéro 1000000000

Une fois le code Je joins une session de la console, dans laquelle je calcule la somme des termes pour les deux constantes (moins PI et 999.999.999) qui existe vraiment dans le matériel, il inséré par le compilateur.

#include <stdio.h>
#include <limits.h>

void
xx(float *x)
{
    unsigned char i = sizeof(*x)*CHAR_BIT-1;
    do {
        switch (i) {
        case 31:
             printf("sign:");
             break;
        case 30:
             printf("exponent:");
             break;
        case 23:
             printf("fraction:");
             break;

        }
        char b=(*(unsigned long long*)x&((unsigned long long)1<<i))!=0;
        printf("%d ", b);
    } while (i--);
    printf("\n");
}

void
yy(float a)
{
    int sign=!(*(unsigned long long*)&a&((unsigned long long)1<<31));
    int fraction = ((1<<23)-1)&(*(int*)&a);
    int exponent = (255&((*(int*)&a)>>23))-127;

    printf(sign?"positive" " ( 1+":"negative" " ( 1+");
    unsigned int i = 1<<22;
    unsigned int j = 1;
    do {
        char b=(fraction&i)!=0;
        b&&(printf("1/(%d) %c", 1<<j, (fraction&(i-1))?'+':')' ), 0);
    } while (j++, i>>=1);

    printf("*2^%d", exponent);
    printf("\n");
}

void
main()
{
    float x=-3.14;
    float y=999999999;
    printf("%lu\n", sizeof(x));
    xx(&x);
    xx(&y);
    yy(x);
    yy(y);
}

Voici une session de la console dans laquelle je calcule la valeur réelle du flotteur qui existe dans le matériel. Je bc d'imprimer la somme des termes par le programme émis principal. On peut insérer cette somme dans repl python ou quelque chose de similaire aussi.

-- .../terra1/stub
@ qemacs f.c
-- .../terra1/stub
@ gcc f.c
-- .../terra1/stub
@ ./a.out
sign:1 exponent:1 0 0 0 0 0 0 fraction:0 1 0 0 1 0 0 0 1 1 1 1 0 1 0 1 1 1 0 0 0 0 1 1
sign:0 exponent:1 0 0 1 1 1 0 fraction:0 1 1 0 1 1 1 0 0 1 1 0 1 0 1 1 0 0 1 0 1 0 0 0
negative ( 1+1/(2) +1/(16) +1/(256) +1/(512) +1/(1024) +1/(2048) +1/(8192) +1/(32768) +1/(65536) +1/(131072) +1/(4194304) +1/(8388608) )*2^1
positive ( 1+1/(2) +1/(4) +1/(16) +1/(32) +1/(64) +1/(512) +1/(1024) +1/(4096) +1/(16384) +1/(32768) +1/(262144) +1/(1048576) )*2^29
-- .../terra1/stub
@ bc
scale=15
( 1+1/(2) +1/(4) +1/(16) +1/(32) +1/(64) +1/(512) +1/(1024) +1/(4096) +1/(16384) +1/(32768) +1/(262144) +1/(1048576) )*2^29
999999999.999999446351872

Voilà. La valeur de 999999999 est en fait

999999999.999999446351872

Vous pouvez également vérifier auprès bc que -3,14 est également perturbé. Ne pas oublier de mettre un facteur de scale dans bc.

La somme affichée est ce que l'intérieur du matériel. La valeur que vous obtenez en calculant cela dépend de l'échelle que vous définissez. Je ne mets le facteur de scale à 15. Mathématiquement, avec une précision infinie, il semble qu'il est 1000000000.

Une autre façon de regarder ceci: utiliser sont 64 bits pour représenter des nombres. En conséquence, il n'y a pas moyen de plus de 2 ** 64 = 18,446,744,073,709,551,616 nombres différents peuvent être représentés avec précision.

Cependant, Math dit il y a déjà un nombre infini de décimales entre 0 et 1. IEE 754 définit un codage à utiliser ces 64 bits efficacement pour un espace beaucoup plus grand nombre, plus NaN et Infinity +/-, donc il y a des lacunes entre représentés avec précision numéros remplis de chiffres seulement approchées.

0,3 Malheureusement se trouve dans un intervalle.

Étant donné que ce fil bifurque un peu dans une discussion générale sur les mises en œuvre à virgule flottante courant j'ajouter qu'il ya des projets sur la fixation de leurs problèmes.

Jetez un oeil à https://posithub.org/ par exemple, qui met en vedette un type de numéro appelé posit (et son prédécesseur unum) qui promet d'offrir une meilleure précision avec moins de bits. Si je comprends bien, il fixe aussi le genre de problèmes dans la question. Tout projet intéressant, la personne derrière elle est un mathématicien, il Dr. John Gustafson . L'ensemble est open source, avec de nombreuses implémentations actuelles en C / C ++, Python, Julia et C # ( https: // hastlayer. com / arithmétique).

Imaginez que vous travaillez dans une base de dix avec, disons, 8 chiffres de précision. Vous vérifiez si

1/3 + 2 / 3 == 1

et apprendre que ce retour false. Pourquoi? Eh bien, comme des nombres réels que nous avons

1/3 = 0,333 .... et 2/3 = 0,666 ....

Tronquer à huit décimales près, nous obtenons

0.33333333 + 0.66666666 = 0.99999999

qui est, bien sûr, différent de 1.00000000 par 0.00000001 exactement.


La situation des nombres binaires avec un nombre fixe de bits est exactement analogue. En chiffres réels, nous avons

1/10 = 0,0001100110011001100 ... (base 2)

et

1/5 = 0,0011001100110011001 ... (base 2)

Si nous tronqués ces, disons, sept bits, nous obtiendrions

0.0001100 + 0.0011001 = 0.0100101

tandis que d'autre part,

3/10 = 0,01001100110011 ... (base 2)

qui, tronqué à sept bits, est 0.0100110, et ceux-ci se distinguent par 0.0000001 exactement.


La situation exacte est un peu plus subtile, car ces chiffres sont généralement stockés dans la notation scientifique. Ainsi, par exemple, au lieu de stocker 1/10 comme 0.0001100 nous pouvons stocker quelque chose comme 1.10011 * 2^-4, selon le nombre de bits que nous avons prévu pour l'exposant et la mantisse. Cela affecte le nombre de chiffres de précision que vous obtenez pour vos calculs.

Le résultat est qu'en raison de ces erreurs d'arrondi que vous voulez essentiellement ne jamais utiliser == sur les nombres à virgule flottante. Au lieu de cela, vous pouvez vérifier si la valeur absolue de leur différence est inférieure à un petit nombre fixe.

Depuis Python 3.5 vous pouvez utiliser la fonction math.isclose() pour tester l'égalité approximative :

>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True
>>> 0.1 + 0.2 == 0.3
False

Math.sum (javascript) .... type de remplacement de l'opérateur

.1 + .0001 + -.1 --> 0.00010000000000000286
Math.sum(.1 , .0001, -.1) --> 0.0001

Object.defineProperties(Math, {
    sign: {
        value: function (x) {
            return x ? x < 0 ? -1 : 1 : 0;
            }
        },
    precision: {
        value: function (value, precision, type) {
            var v = parseFloat(value), 
                p = Math.max(precision, 0) || 0, 
                t = type || 'round';
            return (Math[t](v * Math.pow(10, p)) / Math.pow(10, p)).toFixed(p);
        }
    },
    scientific_to_num: {  // this is from https://gist.github.com/jiggzson
        value: function (num) {
            //if the number is in scientific notation remove it
            if (/e/i.test(num)) {
                var zero = '0',
                        parts = String(num).toLowerCase().split('e'), //split into coeff and exponent
                        e = parts.pop(), //store the exponential part
                        l = Math.abs(e), //get the number of zeros
                        sign = e / l,
                        coeff_array = parts[0].split('.');
                if (sign === -1) {
                    num = zero + '.' + new Array(l).join(zero) + coeff_array.join('');
                } else {
                    var dec = coeff_array[1];
                    if (dec)
                        l = l - dec.length;
                    num = coeff_array.join('') + new Array(l + 1).join(zero);
                }
            }
            return num;
         }
     }
    get_precision: {
        value: function (number) {
            var arr = Math.scientific_to_num((number + "")).split(".");
            return arr[1] ? arr[1].length : 0;
        }
    },
    diff:{
        value: function(A,B){
            var prec = this.max(this.get_precision(A),this.get_precision(B));
            return +this.precision(A-B,prec);
        }
    },
    sum: {
        value: function () {
            var prec = 0, sum = 0;
            for (var i = 0; i < arguments.length; i++) {
                prec = this.max(prec, this.get_precision(arguments[i]));
                sum += +arguments[i]; // force float to convert strings to number
            }
            return Math.precision(sum, prec);
        }
    }
});

l'idée est d'utiliser les mathématiques plutôt que les opérateurs pour éviter les erreurs de flotteur

Math.diff(0.2, 0.11) == 0.09 // true
0.2 - 0.11 == 0.09 // false

noter également que l'auto-détecter la précision Math.diff et Math.sum utiliser

Math.sum accepte un certain nombre d'arguments

  

Il est en fait assez simple. Lorsque vous avez une base 10 système (comme la nôtre), il ne peut exprimer des fractions qui utilisent un facteur premier de la base. Les principaux facteurs de 10 sont 2 et 5. Donc, 1/2, 1/4, 1/5, 1/8 et 1/10 peuvent tous être exprimés proprement parce que les dénominateurs utilisent tous les facteurs premiers de 10. En revanche, 1 / 3, 1/6 et 1/7 sont tous les répéter parce que leurs décimaux dénominateurs utilisent un facteur premier de 3 ou 7. En binaire (ou base 2), le seul facteur premier est 2. Ainsi, vous pouvez seulement exprimer des fractions proprement qui ne contiennent 2 en tant que facteur premier. En binaire, 1/2, 1/4, 1/8 seraient tous exprimés proprement en décimales. Bien que, 1/5 ou 1/10 serait répéter décimaux. Ainsi, 0,1 et 0,2 (1/10 et 1/5), tandis que les décimales propres dans un système de base 10, répètent dans la base de décimales 2 système de l'ordinateur fonctionne. Lorsque vous faites des mathématiques sur ces décimales répéter, vous vous retrouvez avec les restes qui portent sur lorsque vous convertissez la base 2 (binaire) de l'ordinateur en un certain nombre de base plus lisible humaine 10.

De https://0.30000000000000004.com/

Une autre question a été désignée comme un double à celui-ci:

En C ++, pourquoi est le résultat d'une cout << x différente de la valeur qu'un débogueur montre pour x?

Le x la question est une variable float.

Un exemple serait

float x = 9.9F;

Le débogueur montre 9.89999962, la sortie de l'opération de cout est 9.9.

La réponse se révèle être que la précision par défaut de cout pour float est de 6, donc il arrondit à 6 chiffres décimaux.

Voir pour référence

Cela a été effectivement conçu comme une réponse à cette question - qui a été fermé en double de < strong> ce question, en Je mettais ensemble cette réponse, maintenant je ne peux pas poster là ... donc je vais poster ici au lieu!


  

Résumé Question:

     

Sur la feuille 10^-8/1000 et 10^-11 évalue en Equal tout en VBA, ils ne le font pas.

Sur la feuille de calcul, les chiffres sont par défaut à la notation scientifique.

Si vous modifiez les cellules à un format numérique ( Ctrl + 1 ) de Number avec des points décimaux 15, vous obtenez:

=10^-11 returns 0.000000000010000
=10^(-8/1000) returns 0.981747943019984

Ainsi, ils ne sont certainement pas le même ... on est à peu près zéro et l'autre à peu près 1.

Excel n'a pas été conçu pour traiter les très un petit nombre - au moins pas avec le stock d'installation. Il y a des add-ins pour aider à améliorer le nombre de précision.


  

Excel a été conçu conformément à la norme IEEE pour arithmétique binaire à virgule flottante ( IEEE 754 ) . La norme définit comment nombres à virgule flottante sont stockées et calculées. norme IEEE 754 est largement utilisée car elle permet-nombres à virgule flottante à stocker dans un délai raisonnable l'espace et les calculs peuvent se produire relativement rapidement.

     

L'avantage de flotter au-dessus de la représentation à virgule fixe est qu'il peut supporter une large gamme de valeurs. Par exemple, une représentation à virgule fixe qui a 5 chiffres décimaux avec la virgule décimale placée après le troisième chiffre peut représenter les nombres 123.34, 12.23, 2.45, etc. alors que la représentation en virgule flottante avec 5 précision de chiffres peut représenter 1,2345, 12345, 0,00012345 , etc. de même, la représentation à virgule flottante permet également des calculs sur une large gamme de grandeurs tout en maintenant la précision. Par exemple,

img


Autres

Références:

Les fractions décimales telles que 0.1, 0.2 et 0.3 ne sont pas représentées exactement en binaire codé types à virgule flottante. La somme des approximations pour 0.1 et 0.2 diffère de l'approximation utilisée pour 0.3, d'où le mensonge de 0.1 + 0.2 == 0.3 comme on peut le voir plus clairement ici:

#include <stdio.h>

int main() {
    printf("0.1 + 0.2 == 0.3 is %s\n", 0.1 + 0.2 == 0.3 ? "true" : "false");
    printf("0.1 is %.23f\n", 0.1);
    printf("0.2 is %.23f\n", 0.2);
    printf("0.1 + 0.2 is %.23f\n", 0.1 + 0.2);
    printf("0.3 is %.23f\n", 0.3);
    printf("0.3 - (0.1 + 0.2) is %g\n", 0.3 - (0.1 + 0.2));
    return 0;
}

Sortie:

0.1 + 0.2 == 0.3 is false
0.1 is 0.10000000000000000555112
0.2 is 0.20000000000000001110223
0.1 + 0.2 is 0.30000000000000004440892
0.3 is 0.29999999999999998889777
0.3 - (0.1 + 0.2) is -5.55112e-17

Pour ces calculs à évaluer de façon plus fiable, vous devez utiliser une représentation à base décimale pour les valeurs à virgule flottante. Le C standard ne précise pas ces types par défaut, mais comme une extension décrite dans Rapport technique. Types _Decimal32, _Decimal64 et _Decimal128 pourrait être disponible sur votre système (par exemple gcc les prend en charge sur noreferrer cibles sélectionnées , mais clang ne les supporte pas OS / X).

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