Java et le type double: quand ça ne passe pas à l'échelle !

D'où l'importance de savoir à l'avance les ordres de grandeur de votre programme

Billet le bonjour à vous,

J’aimerai vous raconter l’histoire d’un programme avec lequel je me suis pris la tête avant de me rendre compte que le problème était entre la chaise et le clavier, et aussi un peu dans la tasse de café ☕ .

Voici à quoi ressemble le morceau de code qui a fait souffrir le rongeur que je suis.

jshell> double v0=1073741824000000000000000000000000000000.0;
v0 ==> 1.073741824E39

jshell> double v1 = v0 - 1.0;
v1 ==> 1.073741824E39

jshell> System.out.println("difference = "+(v0 - v1));
difference = 0.0

😮

Je pense que vous aurez compris que je m’attendais à ce qu’à la ligne 8, la différence m’affiche la valeur 1.0 au lieu de 0.0. Que nenni ! J’ai été trompé !

L’explication

En Java lorsqu’on dépasse une certaine valeur (comme c’est mon cas) le type double n’est plus utilisable et pour ça, il faut utiliser le type BigDecimal.

Cependant, on ne peut pas prendre comme choix par défaut dans tous nos programmes, le type BigDecimal car les calculs avec ce dernier sont plus lent que ceux avec le type double.

Sauf que quand vous êtes comme moi et que : 1. vous commencez à écrire votre programme en testant avec des petits nombres, 2. vous embarquez votre code dans un fonction qui semble bien faire le job, 3. le résultat de la fonction dépend de l’entrée utilisateur

Le résultat devient imprévisible.

Que fait la police le compilo ?

Ce que je ne comprend pas, c’est qu’en 2021, le compilateur ne sait pas me prévenir d’un tel danger et que la JVM ne lève aucune exception sur ce genre de cas (même mon éditeur préféré ne m’a rien remonté).

La variable du mauvais type (n’y voyez aucune jeu de mot) continue d’accepter une valeur qu’elle ne peut supporter et le seul moyen de se rendre compte du problème c’est d’avoir eu la lucidité de tester avec la valeur qui donne le mauvais résultat (sans planter le programme).



Souvenez-vous donc qu’en Java, le type double (et int d’ailleurs) ne passe pas à l’échelle, mais est trop timide pour nous l’avouer.

Maintenant j’en suis à me demander si ma banque utilise le bon type de donnée quand elle calcule mes intérêts. 😱

Et vous, comment c’est géré dans votre langage préféré ?

30 commentaires

Ah je pensais l’avoir dit dans le billet, je ne sais pas si j’ai le droit d’en parler publiquement pour le moment. D’ou le fait que je ne présente que la partie technique et non fonctionnelle désolé.

EDIT

Edit: ça paraît un peu bizarre de faire un parcours BFS avec Spark, tu n’es pas tué par les coûts de synchronization ? Ou alors il y a beaucoup de calcul pour chaque noeud du graphe ? (Ça ne semble pas réaliste si le graphe est très gros.)

Tant que tu évite de faire des opérations terminales trop souvent ça ne pose pas de problème. Et à la base, le graphe n’est pas censé être aussi gros, le besoin a "évolué" entre temps.

Mais en même temps il y a une dimension qui manque à l’article et à la discussion : c’est quoi, au juste, le code de @firm1 qui fait des manipulations sur des nombres si grands que les double perdent en précision ? Avec les types simple-précision (float plutôt que double), on a des soucis sur des grandeurs sur lesquelles on peut assez naturellement tomber sur des programmes de la vie courante. Mais avec les doubles, c’est moins évident ! Personellement je n’ai jamais eu besoin d’écrire un programme qui a besoin d’être précis sur les calculs sur une grandeur d’ordre de 1.07E39.

Tu n’as pas besoin de monter aussi haut pour avoir des problèmes potentiels d’absorption/d’accumulation d’erreur sur les flottants. C’est un problème relativement classique en intégration par exemple où tu accumules plein de petits nombres et si tu ne fais pas attention, les petits nombres que tu accumules à la fin (alors que ton résultat courant a commencé à grossir) sont partiellement absorbés. Sur une seule de ces accumulations, ça ne se voit pas. Injecte ça dans un code qui fait des accumulations de millions de nombres quelque centaines de milliers de fois (par exemple parce que tu intègres spatialement des champs puis tu intègres temporellement cette intégrale spatiale), et tu peux vite accumuler des erreurs substantielles ou qui au moins viennent plomber le comportement asymptotique de l’erreur de ton schéma numérique. Ça vient casser facilement la baraque lorsque tu essaies de construire des expériences numériques reproductibles (encore plus lorsque tu résous des équations au comportement fondamentalement chaotique comme en météo).

C’est d’ailleurs la raison pour laquelle les banques n’utilisent pas des nombres flottants mais e.g. des représentations à virgule fixe/de taille variable pour ne pas prendre le risque d’accumuler plein d’erreurs minuscules sur les milliards d’opérations qu’elles gèrent.

+1 -0

Oui, tu parles de problème de stabilité numérique sur des simulations, qui sont bien connus de gens du domaine. On peut citer d’autres domaines où la précision est importante, par exemple les algorithmes géométriques (manipulation de quantités très petites). Pour moi l’idée n’est pas de dire que de tels domaines n’existent pas (évidemment ils existent), mais plutôt que la plupart des programmeurs les rencontrent rarement; et donc que lier ce billet "retour d’expérience" à précisément un besoin précis permettrait de le rendre plus concret et plus intéressant.

Oui, tu parles de problème de stabilité numérique sur des simulations, qui sont bien connus de gens du domaine.

Non, je parle de problèmes inhérents aux calculs flottants qui ne sont qu’un sous-ensemble très réduit des problèmes de stabilité numérique. Le gros de cette problématique provient de la discrétisation d’équations continues et existe même en manipulant des nombres réels. La plupart des calculs de stabilité numérique sont faits en ignorant largement la question de la précision des flottants par ailleurs (qui n’est d’ailleurs pas toujours bien connue des "gens du domaine" qui souvent ne sont pas numériciens de formation mais travaillent avec de tels outils).

Pour moi l’idée n’est pas de dire que de tels domaines n’existent pas (évidemment ils existent), mais plutôt que la plupart des programmeurs les rencontrent rarement

Tu demandais un exemple plus haut, j’en donne un concret.

+1 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte