11521 sujets

JavaScript, DOM et API Web HTML5

Je me heurte à un problème d'écart d'arrondi entre JavaScript et Excel :

Math.floor(0.5384615384615384/0.07692307692307693)

...produit 6 en JavaScript (Firefox 47.1).

Mais :

ARRONDI.INF(0,538461538461538/0,0769230769230769;0)

... produit 7 dans Excel (Excel 2000).


D'où quelques questions :

1/ Qui a raison ?

2/ Quelqu'un observerait-il cet écart avec d'autres versions de Firefox et d'Excel ?

3/ Quelqu'un aurait-il la source d'une explication (visiblement une limite dans la gestion des flottants, mais documentée où ?)
Modérateur
Il y a évidemment des problèmes de flottants en javscript. Il existe des librairies pour contourner le problème,

j'en ai testé deux qui semblent relativement d'accord:

http://codepen.io/anon/pen/jAggwK

résultat de la division:
en JS : 6.999999999999999
en js + big.js: 6.99999999999999857
en js avec BigDecimal: 6.9999999999999986
par un calculateur précis: 6.99999999999999857

Dans tous les cas le résultat est 6.

Par contre Excell semble avoir une limite de précision de 15 (https://en.wikipedia.org/wiki/Numeric_precision_in_Microsoft_Excel), ce qui fait que la division est évaluée à 7 avant même l'arrondi Smiley smile
Modifié par kustolovic (29 Aug 2016 - 20:44)
Je suis contre l'avis précédent !

Je soupçonne très fortement que:
0.5384615384615384 provienne de 7/13
et
0.07692307692307693 provienne de 1/13

Si tel est réellement le cas, alors la bonne réponse est 7/13 / 1/13 = 7/13 * 13/1 = 7. C'est excel qui a raison.

Ceci montre une chose, c'est que les notions d'arrondis sont très sensibles, et surtout, peuvent intervenir à toutes et n'importe quelles étapes du calcul.

Techniquement, le seul moyen d'éviter les erreurs d'arrondi, c'est de ne surtout pas utiliser de nombres à virgule flottante, car quelque soit la précision il y a toujours un risque. C'est inhérant à la façon qu'on a de représenter les nombres à virgule flottante en informatique, et le même problème se pose avec tous les langages de programmation et tous les processeurs, pas seulement JavaScript ou les processeurs x86 ou x64. Je ne vais pas faire de blabla sur le binaire, la représentation des nombres selon IEE754 ou la notation positionnelle car il faudrait un tutoriel entier mais ceux qui connaissent les trois ont compris où je veux en venir, les erreurs sont inévitables. IL y a simplement des cas où elles sont plus ou moins acceptables (physique, simulation) et des cas où elles ne le sont pas (finances notamment).
A la place, il faut utiliser une représentation en virgule fixe, c'est ce que font les classes du type BigDecimal.

Pour info, la précision des nombres en JavaScript se situe entre 14 et 16 décimales le plus souvent, car utilisant en interne le type double sur 64 bits selon la norme IEE754. Très probablement la même qu'excel en fait. D'ailleurs les nombres que tu proposes ont comme par hasard exactement cette précision.

Excel est peut-être juste un peu plus malin, il arrive peut-être à constater que ce que tu lui as donné en entrée ne sont autres que des nombres périodiques artificiellement arrondis. Un algorithme simple pour le découvrir pourrait être le suivant:
1. A l'aide de regex, on peut trouver une périodicité, p.ex. pour 0.5384615384615384 on tombe sur 538461. Trouvable avec une regex du style (\d+)\1+
2. Pour trouver la valeur, on prend 538461 en numérateur, et comme dénominateur on prend un nombre de la même longueur avec que des 9, donc ici 538461/999999
3. En simplifiant on tombe sur 7/13 (538461 = 3^3 * 7^2 * 11 * 37 et 999999 = 3^3 * 7 * 11 * 13 * 37)

Mon avis est différent mais ma conclusion est la même:
Si la précision est importante pour toi, utilise toujours ces classes spécialisées, et comme les nombres à virgule flottante sont utilisés par défaut et inévitables en JavaScript, n'effectue jamais de division ou de multiplication sans elles.
Modifié par QuentinC (30 Aug 2016 - 14:59)
Modérateur
J'aurais dû me douter que les valeurs étaient le résultat d'un calcul précédent (et donc incomplètes)

Effectivement: (7/13)/(1/13) donne 6.9999999 en javascript et donc 6, mais donne bien 7 avec les librairies adéquates. Alors qu'Excell le gère bien. Par contre avec les valeurs arrondies telles que fournies en exemple, c'est bien Excell qui se plante sur l'arrondi.