J'ai le script de test factice suivant:
function test() {
var x = 0.1 * 0.2;
document.write(x);
}
test();
Cela imprimera le résultat 0.020000000000000004
alors qu'il devrait simplement imprimer 0.02
(si vous utilisez votre calculatrice). Pour autant que je sache, cela est dû à des erreurs dans la précision de multiplication en virgule flottante.
Quelqu'un a-t-il une bonne solution pour que dans ce cas j'obtienne le bon résultat 0.02
? Je sais qu'il y a des fonctions comme toFixed
ou l'arrondi serait une autre possibilité, mais j'aimerais vraiment que le nombre entier soit imprimé sans couper ni arrondir. Je voulais juste savoir si l'un de vous a une solution élégante et agréable.
Bien sûr, sinon je vais arrondir à environ 10 chiffres.
0.1
à un nombre à virgule flottante binaire fini.Réponses:
Du guide à virgule flottante :
Notez que le premier point ne s'applique que si vous avez vraiment besoin d'un comportement décimal précis et précis . La plupart des gens n'en ont pas besoin, ils sont simplement irrités que leurs programmes ne fonctionnent pas correctement avec des nombres comme 1/10 sans se rendre compte qu'ils ne cligneront même pas à la même erreur si elle se produisait avec 1/3.
Si le premier point s'applique vraiment à vous, utilisez BigDecimal pour JavaScript , qui n'est pas élégant du tout, mais résout en fait le problème plutôt que de fournir une solution de contournement imparfaite.
la source
console.log(9332654729891549)
réellement imprime9332654729891548
(c.-à-d. Par un!);P
... Entre2⁵²
=4,503,599,627,370,496
et2⁵³
=9,007,199,254,740,992
les nombres représentables sont exactement les entiers . Pour la plage suivante, de2⁵³
à2⁵⁴
, tout est multiplié par2
, donc les nombres représentables sont les nombres pairs , etc. Inversement, pour la plage précédente de2⁵¹
à2⁵²
, l'espacement est0.5
, etc. Ceci est dû à une simple augmentation | diminution de la base | radix 2 | exposant binaire dans / de la valeur flottante 64 bits (ce qui explique à son tour le comportement "inattendu" rarement documenté destoPrecision()
valeurs entre0
et1
).J'aime la solution de Pedro Ladaria et j'utilise quelque chose de similaire.
Contrairement à la solution Pedros, cela arrondira à 0,999 ... répétition et est précis à plus / moins un sur le chiffre le moins significatif.
Remarque: Lorsque vous traitez avec des flottants 32 ou 64 bits, vous devez utiliser toPrecision (7) et toPrecision (15) pour de meilleurs résultats. Voir cette question pour savoir pourquoi.
la source
toPrecision
renvoie une chaîne au lieu d'un nombre. Ce n'est pas toujours souhaitable.(9.99*5).toPrecision(2)
= 50 au lieu de 49,95 car toPrecision compte le nombre entier, pas seulement les décimales. Vous pouvez ensuite utilisertoPrecision(4)
, mais si votre résultat est> 100, alors vous n'avez plus de chance, car cela permettra aux trois premiers chiffres et à une décimale, de cette façon de déplacer le point et de le rendre plus ou moins inutilisable. J'ai fini par utiliser à latoFixed(2)
placePour les personnes mathématiquement inclinées: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
L'approche recommandée est d'utiliser des facteurs de correction (multiplier par une puissance appropriée de 10 pour que l'arithmétique se fasse entre les entiers). Par exemple, dans le cas de
0.1 * 0.2
, le facteur de correction est10
et vous effectuez le calcul:Une solution (très rapide) ressemble à:
Dans ce cas:
Je recommande vraiment d'utiliser une bibliothèque testée comme SinfulJS
la source
Faites-vous uniquement de la multiplication? Si c'est le cas, vous pouvez utiliser à votre avantage un bon secret sur l'arithmétique décimale. Voilà
NumberOfDecimals(X) + NumberOfDecimals(Y) = ExpectedNumberOfDecimals
. C'est-à-dire que si nous avons0.123 * 0.12
alors nous savons qu'il y aura 5 décimales car0.123
a 3 décimales et en0.12
a deux. Ainsi, si JavaScript nous a donné un nombre tel que0.014760000002
nous pouvons arrondir en toute sécurité à la 5ème décimale sans craindre de perdre en précision.la source
Vous recherchez une
sprintf
implémentation pour JavaScript, afin que vous puissiez y écrire des flottants avec de petites erreurs (car ils sont stockés au format binaire) dans un format que vous attendez.Essayez javascript-sprintf , vous l' appeleriez ainsi:
pour imprimer votre numéro sous forme de flottant avec deux décimales.
Vous pouvez également utiliser Number.toFixed () à des fins d'affichage, si vous préférez ne pas inclure plus de fichiers simplement pour l'arrondi à virgule flottante avec une précision donnée.
la source
Je trouve que BigNumber.js répond à mes besoins.
Il a une bonne documentation et l'auteur répond très diligemment aux commentaires.
Le même auteur possède 2 autres bibliothèques similaires:
Big.js
et Decimal.js
Voici du code utilisant BigNumber:
la source
---ou---
---aussi---
--- un péché ---
la source
Cette fonction déterminera la précision requise à partir de la multiplication de deux nombres à virgule flottante et retournera un résultat avec la précision appropriée. Élégant même s'il ne l'est pas.
la source
Étonnamment, cette fonction n'a pas encore été publiée bien que d'autres en aient des variantes similaires. Il provient des documents Web MDN pour Math.round (). Il est concis et permet une précision variable.
console.log (precisionRound (1234.5678, 1)); // sortie attendue: 1234,6
console.log (precisionRound (1234.5678, -1)); // sortie attendue: 1230
MISE À JOUR: 20 août 2019 Je viens de remarquer cette erreur. Je pense que c'est dû à une erreur de précision en virgule flottante avec Math.round ().
Ces conditions fonctionnent correctement:
Réparer:
Cela ajoute juste un chiffre à droite lors de l'arrondi des décimales. MDN a mis à jour la page Math.round afin que quelqu'un puisse peut-être fournir une meilleure solution.
la source
Vous n'avez qu'à vous décider sur le nombre de chiffres décimaux que vous voulez réellement - vous ne pouvez pas avoir le gâteau et le manger aussi :-)
Des erreurs numériques s'accumulent à chaque nouvelle opération et si vous ne les interrompez pas tôt, cela ne fera qu'augmenter. Les bibliothèques numériques qui présentent des résultats qui ont l'air propres coupent simplement les 2 derniers chiffres à chaque étape, les coprocesseurs numériques ont également une longueur "normale" et "complète" pour la même raison. Les cuf-offs sont bon marché pour un processeur mais très chers pour vous dans un script (multiplier et diviser et utiliser pov (...)). Une bonne bibliothèque de mathématiques fournirait un plancher (x, n) pour faire la coupure pour vous.
Donc, au minimum, vous devriez faire var / constant global avec pov (10, n) - ce qui signifie que vous avez décidé de la précision dont vous avez besoin :-) Ensuite, faites:
Vous pouvez également continuer à faire des calculs et à ne couper qu'à la fin - en supposant que vous affichez uniquement et ne faites pas de if avec des résultats. Si vous pouvez le faire, alors .toFixed (...) pourrait être plus efficace.
Si vous faites des comparaisons if-s / et que vous ne voulez pas couper, vous avez également besoin d'une petite constante, généralement appelée eps, qui est une décimale plus élevée que l'erreur maximale attendue. Supposons que votre seuil de coupure correspond aux deux dernières décimales - alors votre eps a 1 à la 3ème place de la dernière (3ème moins significatif) et vous pouvez l'utiliser pour comparer si le résultat est dans la plage eps attendue (0,02 -eps <0,1 * 0,2 <0,02 + eps).
la source
Math.floor(-2.1)
est-3
. Alors peut-être utiliser par exempleMath[x<0?'ceil':'floor'](x*PREC_LIM)/PREC_LIM
floor
au lieu deround
?Vous pouvez utiliser
parseFloat()
ettoFixed()
si vous souhaitez contourner ce problème pour une petite opération:la source
La fonction round () de phpjs.org fonctionne bien: http://phpjs.org/functions/round
la source
0.6 * 3 c'est génial!)) Pour moi, cela fonctionne bien:
Très très simple))
la source
8.22e-8 * 1.3
?Notez que pour un usage général, ce comportement est susceptible d'être acceptable.
Le problème se pose lors de la comparaison de ces valeurs à virgule flottante pour déterminer une action appropriée.
Avec l'avènement d'ES6, une nouvelle constante
Number.EPSILON
est définie pour déterminer la marge d'erreur acceptable:donc au lieu d'effectuer la comparaison comme celle-ci
vous pouvez définir une fonction de comparaison personnalisée, comme ceci:
Source: http://2ality.com/2015/04/numbers-math-es6.html#numberepsilon
la source
0.9 !== 0.8999999761581421
Le résultat que vous avez est correct et assez cohérent sur toutes les implémentations en virgule flottante dans différents langages, processeurs et systèmes d'exploitation - la seule chose qui change est le niveau de l'inexactitude lorsque le flottant est en fait un double (ou supérieur).
0,1 en virgule flottante binaire équivaut à 1/3 en décimal (c'est-à-dire 0,3333333333333 ... pour toujours), il n'y a tout simplement aucun moyen précis de le gérer.
Si vous traitez toujours avec des flotteurs attendez-vous à de petites erreurs d'arrondi, vous devrez donc toujours arrondir le résultat affiché à quelque chose de sensible. En retour, vous obtenez une arithmétique très très rapide et puissante car tous les calculs sont dans le binaire natif du processeur.
La plupart du temps, la solution n'est pas de passer à l'arithmétique à virgule fixe, principalement parce que c'est beaucoup plus lent et 99% du temps, vous n'avez tout simplement pas besoin de la précision. Si vous avez affaire à des choses qui nécessitent ce niveau de précision (par exemple, les transactions financières), Javascript n'est probablement pas le meilleur outil à utiliser de toute façon (comme vous voulez appliquer les types à virgule fixe, un langage statique est probablement mieux ).
Vous cherchez la solution élégante, alors je crains que ce soit le cas: les flotteurs sont rapides mais ont de petites erreurs d'arrondi - toujours arrondis à quelque chose de sensé lors de l'affichage de leurs résultats.
la source
Pour éviter cela, vous devez travailler avec des valeurs entières au lieu de virgules flottantes. Donc, lorsque vous voulez avoir une précision de 2 positions, travaillez avec les valeurs * 100, pour 3 positions, utilisez 1000. Lors de l'affichage, vous utilisez un formateur pour mettre dans le séparateur.
De nombreux systèmes omettent de travailler avec des décimales de cette façon. C'est la raison pour laquelle de nombreux systèmes fonctionnent avec des cents (en entier) au lieu de dollars / euros (en virgule flottante).
la source
Problème
Le point flottant ne peut pas stocker toutes les valeurs décimales exactement. Ainsi, lors de l'utilisation de formats à virgule flottante, il y aura toujours des erreurs d'arrondi sur les valeurs d'entrée. Les erreurs sur les entrées résultent bien sûr d'erreurs sur la sortie. Dans le cas d'une fonction ou d'un opérateur discret, il peut y avoir de grandes différences sur la sortie autour du point où la fonction ou l'opérateur est discret.
Entrée et sortie pour les valeurs à virgule flottante
Ainsi, lorsque vous utilisez des variables à virgule flottante, vous devez toujours en être conscient. Et quelle que soit la sortie que vous souhaitez d'un calcul à virgule flottante, elle doit toujours être formatée / conditionnée avant d'être affichée dans cet esprit.
Lorsque seules des fonctions et des opérateurs continus sont utilisés, l'arrondi à la précision souhaitée fera souvent l'affaire (ne pas tronquer). Les fonctionnalités de formatage standard utilisées pour convertir les flottants en chaîne le feront généralement pour vous.
Étant donné que l'arrondi ajoute une erreur qui peut entraîner une erreur totale supérieure à la moitié de la précision souhaitée, la sortie doit être corrigée en fonction de la précision attendue des entrées et de la précision souhaitée de la sortie. Vous devriez
Ces 2 choses ne sont généralement pas effectuées et dans la plupart des cas, les différences causées par le fait de ne pas les faire sont trop petites pour être importantes pour la plupart des utilisateurs, mais j'avais déjà un projet où la sortie n'était pas acceptée par les utilisateurs sans ces corrections.
Fonctions ou opérateurs discrets (comme le module)
Lorsque des opérateurs ou des fonctions discrets sont impliqués, des corrections supplémentaires peuvent être nécessaires pour s'assurer que la sortie est conforme aux attentes. L'arrondi et l'ajout de petites corrections avant l'arrondi ne peuvent pas résoudre le problème.
Une vérification / correction spéciale des résultats de calcul intermédiaires, immédiatement après l'application de la fonction discrète ou de l'opérateur, peut être requise. Pour un cas spécifique (opérateur de module), voir ma réponse à la question: Pourquoi l'opérateur de module renvoie-t-il un nombre fractionnaire en javascript?
Mieux vaut éviter d'avoir le problème
Il est souvent plus efficace d'éviter ces problèmes en utilisant des types de données (formats entiers ou à virgule fixe) pour des calculs comme celui-ci qui peuvent stocker l'entrée attendue sans erreurs d'arrondi. Un exemple de cela est que vous ne devez jamais utiliser de valeurs à virgule flottante pour les calculs financiers.
la source
Jetez un oeil à l' arithmétique à virgule fixe . Cela résoudra probablement votre problème si la plage de nombres sur laquelle vous souhaitez opérer est petite (par exemple, la devise). Je voudrais l'arrondir à quelques valeurs décimales, ce qui est la solution la plus simple.
la source
Essayez ma bibliothèque d'arithmétique chiliadique, que vous pouvez voir ici . Si vous voulez une version ultérieure, je peux vous en procurer une.
la source
Vous ne pouvez pas représenter exactement la plupart des fractions décimales avec des types binaires à virgule flottante (ce que ECMAScript utilise pour représenter les valeurs à virgule flottante). Il n'y a donc pas de solution élégante à moins que vous n'utilisiez des types arithmétiques à précision arbitraire ou un type à virgule flottante basé sur des décimales. Par exemple, l'application Calculatrice fournie avec Windows utilise désormais une arithmétique de précision arbitraire pour résoudre ce problème .
la source
la source
Vous avez raison, la raison en est la précision limitée des nombres à virgule flottante. Stockez vos nombres rationnels comme une division de deux nombres entiers et dans la plupart des situations, vous pourrez stocker des nombres sans perte de précision. En ce qui concerne l'impression, vous souhaiterez peut-être afficher le résultat sous forme de fraction. Avec la représentation que j'ai proposée, cela devient trivial.
Bien sûr, cela n'aidera pas beaucoup avec des nombres irrationnels. Mais vous voudrez peut-être optimiser vos calculs de manière à ce qu'ils causent le moins de problèmes (par exemple, détecter des situations comme
sqrt(3)^2)
.la source
<pedant>
fait, l'OP l'a mis à des opérations imprécises en virgule flottante, ce qui est faux</pedant>
J'ai eu un problème d'erreur d'arrondi désagréable avec le mod 3. Parfois, quand je devrais obtenir 0, j'obtiens 0,000 ... 01. C'est assez facile à gérer, testez simplement <= .01. Mais parfois, j'obtenais 2,9999999999999998. AIE!
BigNumbers résolu le problème, mais a introduit un autre problème quelque peu ironique. En essayant de charger 8.5 dans BigNumbers, j'ai été informé que c'était vraiment 8.4999… et avait plus de 15 chiffres significatifs. Cela signifiait que BigNumbers ne pouvait pas l'accepter (je crois avoir mentionné que ce problème était quelque peu ironique).
Solution simple à un problème ironique:
la source
Utilisez le numéro (1.234443) .àFixed (2); il imprimera 1.23
la source
decimal.js , big.js ou bignumber.js peuvent être utilisés pour éviter les problèmes de manipulation à virgule flottante en Javascript:
lien vers des comparaisons détaillées
la source
Élégant, prévisible et réutilisable
Traitons le problème de manière élégante et réutilisable. Les sept lignes suivantes vous permettront d'accéder à la précision en virgule flottante que vous désirez sur n'importe quel nombre simplement en ajoutant
.decimal
à la fin du nombre, de la formule ou de laMath
fonction intégrée.À votre santé!
la source
Utilisation
la source
Math.round(x*Math.pow(10,4))/Math.pow(10,4);
. L'arrondi est toujours une option, mais je voulais juste savoir s'il y avait une meilleure solutionpas élégant mais fait le travail (supprime les zéros de fin)
la source
Cela fonctionne pour moi:
la source
Sortie en utilisant la fonction suivante:
Faites attention à la sortie
toFixedCurrency(x)
.la source