Erreur de virgule flottante Java

** Le code de cet article est écrit en Java SE 8. ** **

Contexte

Si vous comparez des variables de type double avec ==, vous remarquerez que ce n'est pas bon avec les outils d'analyse statique, c'est un bogue. J'étais curieux de connaître diverses choses, alors j'ai essayé de savoir comment gérer les nombres à virgule flottante (maintenant).

Conclusion d'abord

Si vous ne vous souciez pas des performances, utilisez Big Decimal. Si vous pouvez modifier la conception de la base de données, vous pouvez déplacer les chiffres et les gérer avec int ou long.

problème

Tout d'abord, jetez un œil à celui-ci.

code

	double doubleValue1 = 1.0d;
	double doubleValue2 = 0.9d;
	double resultValue = doubleValue1 - doubleValue2;

	System.out.println("doubleValue1: " + doubleValue1);
	System.out.println("doubleValue2: " +  doubleValue2);
	System.out.println(resultValue == 0.1d);
	System.out.println("Résultat de la soustraction: " + resultValue);

** Résultat d'exécution **

	doubleValue1: 1.0
	doubleValue2: 0.9
	false
Résultat de la soustraction: 0.09999999999999998

Le résultat de la soustraction est censé être excellent.

Les ordinateurs traitent les nombres en binaire.

Cependant, certaines fractions, telles que "0,9", deviennent des fractions circulaires lorsqu'elles sont exprimées en binaire. Étant donné que l'ordinateur ne peut gérer qu'un nombre fini de chiffres, il est arrondi à une valeur appropriée et une erreur se produit.

Puisqu'il est subtilement soustrait avec une valeur autre que "0,9", le résultat ci-dessus sera obtenu. Dans le système comptable, même une erreur de 1 yen peut conduire à un procès, ce qui est un gros problème.

Solution

Utilisez BigDecimal pour résoudre le problème!

code

	BigDecimal bigDecimalValue1 = new BigDecimal("1.0");
	BigDecimal bigDecimalValue2 = new BigDecimal("0.9");
	BigDecimal bigDecimalResultValue = bigDecimalValue1.subtract(bigDecimalValue2);

	System.out.println("bigDecimalValue1: " + bigDecimalValue1);
	System.out.println("bigDecimalValue2: " + bigDecimalValue2);
	System.out.println(bigDecimalResultValue.equals(new BigDecimal("0.1")));
	System.out.println("Résultat de la soustraction: " + bigDecimalResultValue);

** Résultat d'exécution **

	bigDecimalValue1: 1.0
	bigDecimalValue2: 0.9
	true
Résultat de la soustraction: 0.1

BigDecimal contient la valeur sous la forme de décalage de la partie fractionnaire vers la partie entière. Puisque tous les nombres entiers peuvent être représentés par des nombres binaires finis, BigDecimal peut être utilisé pour calculer et comparer avec précision les nombres à virgule flottante.

La méthode est Reportez-vous à la référence officielle.

la vitesse

Étant donné que BigDecimal est un type de référence, il est naturellement plus lent que les opérations de type de données de base. Allons vérifier.

code

	double doubleValue1 = 1.0d;
	double doubleValue2 = 0.9d;

	long startDouble = System.currentTimeMillis();

	//100 millions de déductions(double)
	for(int i = 0; i < 100_000_000 ; i++){
		double resultValue = doubleValue1 - doubleValue2;
	}

	long endDouble = System.currentTimeMillis();

	BigDecimal bigDecimalValue1 = new BigDecimal("1.0");
	BigDecimal bigDecimalValue2 = new BigDecimal("0.9");

	long startBigDecimal = System.currentTimeMillis();

	//100 millions de déductions(BigDecimal)
	for(int i = 0; i < 100_000_000 ; i++){
		BigDecimal bigDecimalResultValue = bigDecimalValue1.subtract(bigDecimalValue2);
	}

	long endBigDecimal = System.currentTimeMillis();

	System.out.println("résultat de mesure double: " + (endDouble - startDouble) + "ms");
	System.out.println("Résultat de la mesure bigDecimal: " + (endBigDecimal - startBigDecimal) + "ms");

** Résultat d'exécution **

résultat de mesure double: 3ms
Résultat de la mesure bigDecimal: 314ms

Il y avait une différence considérable. Après tout, le type de données de base est rapide!

Eh bien, je me suis personnellement demandé s'il n'y aurait pas de problème avec Big Decimal à moins qu'il y ait beaucoup de boucles ou que des performances soient requises.

Si tu t'enfuis, fuis

Il y a des moments où vous n'avez pas à vous forcer à utiliser des nombres à virgule flottante.

Par exemple, supposons que vous ayez un système qui enregistre votre poids jusqu'au premier chiffre de la fraction. Même s'il est reçu à 65,3 kg en termes d'interface, il peut être traité normalement avec int s'il est décalé de un dans la partie amont du programme et manipulé en interne à 653. Libre de traitement en virgule flottante gênant!

Cependant, cela affecte également la définition de colonne de la base de données, il est donc bon de décider fermement au stade de la conception.

Recommended Posts

Erreur de virgule flottante Java
Erreur java d'aujourd'hui
contre-mesures d'erreur java
Pièges d'erreur Java Math.sqrt
Évitez l'erreur que Yuma a donnée en Java
Qu'est-ce qu'une virgule flottante?
résolution d'erreur du getter java setter
Erreur lors de la lecture avec java
Résumé du traitement des erreurs Java
Gestion de la virgule flottante java Lors de la lecture du livre de référence [Note]
[Note] Gestion des points décimaux Java