[JAVA] N'arrondissez pas facilement le résultat d'un calcul fractionnaire

Si vous faites de la programmation, vous avez peut-être entendu dire que "les nombres à virgule flottante ont une erreur".

Mais en êtes-vous toujours conscient? Je n'étais pas au courant et je l'ai vécu.

Exemples courants

Un jour, j'ai dû gagner 0,5% d'intérêt sur le solde de dépôt d'un utilisateur, arrondi au nombre entier inférieur le plus proche. J'ai écrit ce code parce que je pensais que c'était un simple calcul.

JavaScript


Math.floor(value * 1.005)

…… ** C'est une erreur. ** **

Essayons de mettre «1000» en «valeur». J'ai envie d'ajouter 0,5% à 1000 donne 1005, mais en réalité cela devient 1004. L'exemple ci-dessus est JavaScript, mais dans la plupart des langues principales, le résultat est le même. Essayons-le à portée de main.

Pourquoi cela arrive-t-il? Si vous essayez de sortir "1000 * 1.005" tel quel, ce sera "1004.9999999999999". Cette valeur n'a tout simplement pas atteint 1005, donc si vous la tronquez, elle sera 1004. Les nombres à virgule flottante ne peuvent pas représenter avec précision 1,005, il semble donc qu'une erreur s'est produite.

Attention aux troncatures et aux arrondis

Cette erreur est vraiment facile à se produire. Tous les exemples suivants ne produisent pas les résultats attendus.

JavaScript


Math.floor(0.6 / 0.2) // 2
0.6 / 0.2             // 2.9999999999999996

JavaScript


//Taxe à la consommation arrondie au nombre entier supérieur le plus proche
Math.ceil(900 * 1.08) // 973
900 * 1.08            // 972.0000000000001

JavaScript


let f = 0;
for (let i = 0; i < 10; i++) {
  f += 0.1;
}
Math.floor(f) // 0
f             // 0.9999999999999999

Il vaut mieux penser que le résultat du calcul d'un nombre à virgule flottante ne sera pas un entier.

solution de contournement

Vous pouvez éviter ce problème en arrondissant les résultats à un chiffre suffisamment bas avant de les arrondir.

JavaScript


//Arrondi au 4e chiffre
Math.floor((value * 1.005).toFixed(3))

Java


Math.floor(String.format("%.3f", value * 1.005))

PHP


floor(round($value * 1.005, 3))

Il peut ne pas convenir aux situations où des calculs rigoureux sont nécessaires, mais pour la plupart des applications, cela devrait être suffisant. Vous souhaiterez peut-être étendre la classe Math afin qu'elle puisse être appelée à partir de l'ensemble de l'application.

17/2/25 postscript

J'ai reçu un commentaire. En utilisant le type Decimal, il est possible d'effectuer des calculs sans erreur sans aucune implémentation étrange. Les classes BigDecimal peuvent être utilisées en standard en Java, et JavaScript possède également une bibliothèque appelée decimal.js. Il vaut mieux utiliser quelque chose comme ça que de l'implémenter vous-même. (Ajout jusqu'à présent)

Résumé

Soyez prudent lors du calcul des fractions.

Recommended Posts

N'arrondissez pas facilement le résultat d'un calcul fractionnaire
Filtrer le résultat de BindingResult [Spring]
Obtenez le résultat de POST en Java
Mesurez facilement la taille des objets Java
Calcul à rebours de la transition de la graine interne de Random
Le trait de soulignement ActiveSupport n'est pas la conversion inverse de camelize
Ne donnez pas jsessionid au premier accès
Comment afficher le résultat du remplissage du formulaire