Raison: JS traite l'addition, la soustraction, la multiplication et la division des décimales selon 2. Sur la base de l'ARG1, la précision de l'ARG2 est élargie ou inversement élargie, de sorte que la situation suivante se produira.
Le problème de l'ajout, de la soustraction, de la multiplication et de la division des points décimaux dans JavaScript (JS) est un bogue en js tel que 0,3 * 1 = 0,29999999999, etc. Le suivant répertorie quatre algorithmes JS qui peuvent parfaitement calculer la précision correspondante.
fonction accdiv (arg1, arg2) {var t1 = 0, t2 = 0, r1, r2; try {t1 = arg1.toString (). Split (".") [1] .length} catch (e) {} try {t2 = arg2.ToString (). Split (".") [1] .length} catch (e) {} avec (math) {r1 = nombre (arg1.tostring (). Remplace (".", ")) r2 = nombre (arg2.ToString (). Remplace (".", ")) return AccMul ((R1 / R2), POW (10, T2-T1)); }} // fonction multiple accmul (arg1, arg2) {var m = 0, s1 = arg1.toString (), s2 = arg2.ToString (); try {m + = s1.split (".") [1] .length} catch (e) {} try {m + = s2.split (".") [1] .length} catch (e) {} return numéro (s1.replace (".", ")) numéro (s2.replace (". ")) / math.pow (10, m)} Accadd (arg1, arg2) {var r1, r2, m; try {r1 = arg1.toString (). Split (".") [1] .length} catch (e) {r1 = 0} try {r2 = arg2 } // fonction de soustraction subtr (arg1, arg2) {var r1, r2, m, n; try {r1 = arg1.toString (). Split (".") [1] .length} catch (e) {r1 = 0} try {r2 = arg2.ToString (). Split (".") [1] .length} catch (e) {r2 = 0} m = math.pow (10, math.max (r1, r2)); n = (r1> = r2)? r1: r2; return ((arg1 * m-arg2 * m) / m) .tofixed (n); }Analysons la perte de précision numérique en JavaScript.
1. Quelques problèmes typiques de la perte de précision numérique JS
1. Ajouter deux numéros de point flottants simples
0,1 + 0,2! = 0,3 // Vrai
Pyromane
Ce n'est vraiment pas un problème de feu, vous pouvez l'essayer avec alerte (haha, plaisante).
Découvrez les résultats du calcul de Java
Jetez un œil à Python
2. Fonctionnement entier grand
999999999999999999 == 10000000000000000001 //?
Pyromane
Les nombres à 16 chiffres et 17 chiffres sont en fait égaux, c'est déraisonnable.
Comme
var x = 9007199254740992x + 1 == x //?
Voir les résultats
Les trois vues ont été renversées à nouveau.
3. Tofixé ne se tournera pas (chrome)
1.335.Tofixé (2) // 1,33
Pyromane
Il y a eu des cas de prix incohérents dans Chrome et d'autres navigateurs en ligne, et c'est précisément en raison du problème de compatibilité tofixé.
2. Raisons de la précision de la perte de nombres JS
Certains nombres ne peuvent pas être exprimés en finalement par les implémentations binaires informatiques et les limites de bit. Tout comme certains nombres irrationnels ne peuvent pas être représentés finalement, comme PI 3.1415926 ..., 1.3333 ..., etc. JS suit la spécification IEEE 754, adopte un stockage à double précision et occupe 64 bits. Comme indiqué
importance
Nombres de points flottants, par ex.
0,1 >> 0,0001 1001 1001 1001… (1001 Boucle infinie) 0,2 >> 0,0011 0011 0011 0011… (0011 Boucle infinie)
À l'heure actuelle, vous ne pouvez le contourner qu'en imitant le système décimal, mais il n'y a que deux systèmes binaires: 0 et 1, il devient donc 0 et tourne-le par 1. C'est la raison fondamentale pour laquelle certains nombres à virgule flottante dans les ordinateurs sont des erreurs et une précision perdue.
La perte de précision de grands entiers est essentiellement la même que les nombres de points flottants. Le bit Mantissa maximum est de 52 bits, donc l'entier maximum qui peut être représenté avec précision dans JS est Math.Pow (2, 53), qui est 9007199254740992.
Plus de 9007199254740992 PERTE PERDRE PROCODENCE
9007199254740992 >> 1000000000000000 ... 000 // Total 53 09007199254740992 + 1 >> 10000000000000000 ... 001 // Au milieu 52 09007199254740992 + 2 >> 100000000000000 .... 010 //
En fait
9007199254740992 + 1 // perdu 9007199254740992 + 2 // non perdu 9007199254740992 + 3 // perdu 9007199254740992 + 4 // non perdu
Les résultats sont présentés sur la figure
Comme mentionné ci-dessus, nous pouvons savoir que les nombres apparemment finis sont infinis dans la représentation binaire de l'ordinateur. En raison de la limite du bit de stockage, il y a "Got Away" et la perte de précision se produit.
Pour une analyse plus approfondie, vous pouvez lire cet article (long et malodorant): ce que chaque informaticien doit savoir sur l'arithmétique à virgule flottante
3. Solution
Pour les entiers, la probabilité de problèmes dans l'avant peut être relativement faible. Après tout, peu d'entreprises doivent utiliser des entiers super-grands. Tant que le résultat du calcul ne dépasse pas les mathématiques (2, 53), la précision ne sera pas perdue.
Pour les décimales, il y a encore de nombreuses chances de problèmes avec le front-end, en particulier dans certains sites Web de commerce électronique qui impliquent des données telles que des montants. Solution: Mettez la décimale dans l'entier (multiple multiple), puis rétrécissez-vous au multiple d'origine (division multiple)
// 0,1 + 0,2 (0,1 * 10 + 0,2 * 10) / 10 == 0,3 // Vrai
Ce qui suit est un objet que j'ai écrit pour bloquer la perte de précision d'addition, de soustraction, de multiplication et de division des décimales. Bien sûr, l'entier converti ne peut pas dépasser 9007199254740992.
/ *** FloatObj contient quatre méthodes: addition, soustraction, multiplication et division, ce qui peut garantir que le calcul du nombre de points flottants ne perd pas la précision ** Nous savons que les calculs du nombre de points flottants auront une perte de précision (ou une erreur d'arrondi). La cause profonde est que certains nombres ne peuvent pas être exprimés dans les limites binaires binaires et implémentant * Ce qui suit est la représentation binaire correspondant à la décimale décimale * 0,1 >> 0,0001 1001 1001 1001 1001… (1001 Boucle infinie) * 0,2 >> 0,0011 0011 0011 0011… (0011 Loop infinie) * Le stockage de chaque type de données dans un ordinateur est une largeur finale. Par exemple, JavaScript utilise des types numériques de stockage 64 bits, de sorte que ceux qui les dépassent seront rejetés. La partie perdue est la partie perdue en précision. ** ** Méthode *** Ajouter / soustraire / multiplier / diviser ** ** Explication *** 0,1 + 0,2 == 0,3000000000000004 (0,0000000000000004) * 0,2 + 0,4 == 0,60000000000000000001 (0,00000000000000001) * 19,98 (0,0000000000000000002 Moins) ** floatObj.add (0,1, 0,2) >> 0,3 * floatObj.Multiply (19.9, 100) >> 1990 ** / var floatObj = function () {/ ** dans un entier et retourner un entier et plusieurs. Par exemple, 3.14 >> 314, le multiple est 100 * @param floatnum {nombre} décimal * @return {object} * {Times: 100, num: 314} * / function toInteger (floatnum) {var ret = {Times: 1, num: 0} if (isInteger (floatnum) {ret.num = floatNUMetTrUrn. '' var dotpos = strfi.indexof ('. La précision n'est pas perdue * Idée: agrandir la décimale en un entier (multiplicateur), effectuer des opérations arithmétiques, puis rétrécir le numéro de fonctionnement décimal (divisé) ** @param a {numéro} OP {String} Type de fonctionnement, avec addition, soustraction, multiplication et division (add / soustraire / multiplier / diviser) ** / fonction fonctionnel (a, b, chiffres, op) {var o1 = tointeger (a) var o2 = toinTeger (b) var n1 = o1.numvar n2 = o2.numvar t1 = o1.Timesvar t2 = o2.timesvar = t1 = o1.Timesvar t2 = o2. T2? T1: T2VAR Result = nullSwitch (op) {case 'add': if (t1 === t2) {// deux décimales sont les mêmes résultats = n1 + n2} else if (t1> t2) {// o1 décimaux (T2 / T1) + N2} Retour Résultat / MaxCase 'Sustract': if (t1 === T2) {result = n1 - n2} else if (t1> t2) {result = n1 - n2 * (t1 / t2)} else {result = n1 * (t2 / t1) - n2} return result / maxcase ' T2) Retour Resultcase 'Divide': result = (n1 / n2) * (t2 / t1) Retour Result}} // Quatre interfaces d'addition, de soustraction, de multiplication et de division Add (a, b, chiffres) {return opération (A, B, Digits, 'Add')} Fonction SoutrAT (A, B, Digits) {return opération (A, B, Digit Digits) {return Operation (A, B, Digits, 'Multiply')} Fonction Divide (A, B, Digits) {Return Operation (A, B, Digits, 'Divide')} // ExportSreturn {Add: Add, soustrait: soustrait, multiplier: multiplier, diviser: divide}} ();Le correctif de Tofixed est le suivant
// Fonction de correction tofixée tofixed (num, s) {var times = math.pow (10, s) var des = num * Times + 0.5des = parseInt (DES, 10) / timereturn des + ''}