Le nombre qui s'incrémente tout seul
Par Delicious Insights • Publié le 13 sept. 2012

Les nombres sont parfois trompeurs, en JavaScript comme ailleurs. Ouvrez donc une console JavaScript et tentez les expressions suivantes :

1234567890          // => 1234567890
999999999999999     // => 999999999999999
9999999999999999    // => 10000000000000000 -- WTF?!

Voyons ensemble la baleine qui se cache sous ce gravier…

Mais quelle magie obscure fait que 999999999999999 se comporte bien, mais pas 9999999999999999 ?

Comme dans de très nombreux langages, nous voilà pris au piège du standard IEEE-754, datant de 1985 (bientôt 30 ans…), qui gouverne la représentation 64-bit des nombres flottants. Vous retrouverez ce souci dans C, C++, Java, PHP, et pour autant que je sache, .NET (C#, VB, etc.) et ColdFusion… Mais uniquement sur les nombres flottants. Les nombres entiers 64-bit vont bien au-delà.

Seulement voilà, JavaScript ne différencie pas entiers et flottants. Le type Number est forcément flottant, c'est pourquoi en JavaScript 5 / 2 === 2.5, alors que les opérandes semblent « entiers ».

IEEE-754 prévoit une double précision (d'où le nom usuel du type associé dans de nombreux langages : double) de 15 chiffres. Au total (avant et après la virgule éventuelle). Dès qu'on dépasse ce nombre de chiffres, la représentation mémoire utilisée commence à faire des arrondis. Et plus on a de chiffres voulus, plus l'arrondi est brutal :

12345678901234567890 // => 12345678901234567000

Au passage, et pour le plaisir, notez que l'approximation n'a pas lieu que sur des très hautes valeurs. Si on tombe sur des valeurs qui ne sont pas représentables pile-poil par IEEE-754, on obtient des trucs comiques :

0.3         // => 0.3
0.1 + 0.2   // => 0.30000000000000004 !

Et oui, comme pour le statut truthy, la valeur seule ne suffit pas à garantir le bon fonctionnement : la façon d'y arriver est tout aussi importante.

Ici, il se trouve que 0.1 est représenté, en interne, par une valeur retranscrite en 0.10000000000000002. Et de même, 0.2 par 0.20000000000000002. Du coup, la somme aboutit à un résultat… étonnant !