Maison  >  Article  >  Java  >  Explication détaillée d'exemples de perte de float et de double précision en Java

Explication détaillée d'exemples de perte de float et de double précision en Java

零下一度
零下一度original
2017-06-28 11:38:431885parcourir

1.La plage de valeurs de int, float, long, double en java

[java] view plain copy

public class TestOutOfBound {

public static void main ( String[] args) {

System.out.println(Integer.MAX_VALUE-(-Integer.MAX_VALUE)); //Débordement de mémoire

System.out . println(Integer.MAX_VALUE); //2 à la puissance 31 -1, 10 chiffres, environ 2 milliards, ce qui n'est peut-être pas suffisant pour l'argent

System.out.println(Integer.MIN_VALUE) ; //Négatif 2 élevé à la puissance 31

System.out.println(Long.MAX_VALUE); //2 élevé à la puissance 64 - 1,19 chiffres, ce qui est très grand et peut être utilisé pour de l'argent . Ci-dessus

System.out.println(Long.MIN_VALUE); //Négatif 2 à la 64e puissance

System.out.println(Float.MAX_VALUE); //2 à la 128e ; power Square -1, 38 chiffres, deux fois plus longs Ceci est principalement utilisé pour des opérations mathématiques simples et précises

System.out.println(Float.MIN_VALUE);

System.out.println(Double.MAX_VALUE); //2 à la puissance 1024 - 1 308 chiffres, soit 10 fois le nombre de chiffres flottants Il est principalement utilisé pour les opérations complexes et les opérations astronomiques

System.out.println(Double.MIN_VALUE); //2 élevé à la puissance -1074

}

}

2.Problème de perte de flottement et de double précision

Exemple :

[java] afficher la copie simple

Exemple : double résultat = 1,0 - 0,9

Inutile de dire ce résultat, nous le connaissons tous, 0,09999999999999998

Pourquoi ce problème se produit-il ? C'est un problème qui se produit en Java et dans d'autres langages informatiques. se produit. Question :
Les types float et double sont principalement conçus pour les calculs scientifiques et les calculs techniques. Ils effectuent une arithmétique binaire à virgule flottante, soigneusement conçue pour fournir des calculs approximatifs rapides avec une grande précision sur une large plage de nombres. Cependant, ils ne fournissent pas de résultats totalement précis et ne doivent pas être utilisés pour des calculs exacts. Les types float et double sont particulièrement inadaptés aux opérations monétaires, car il est impossible pour un float ou un double de représenter avec précision 0,1 ou toute autre puissance négative de 10 (en fait, la raison est très simple. Peut-il être représenté avec précision au format décimal ? système ? Qu’en est-il de 1/3 ? Le même système binaire ne peut pas représenter avec précision 1/10).

Les opérations en virgule flottante sont rarement précises et des erreurs se produiront tant qu'elles dépassent la plage que la précision peut représenter. L’erreur se produit souvent non pas à cause de la taille du nombre, mais à cause de la précision du nombre. Par conséquent, les résultats produits sont proches mais pas égaux aux résultats souhaités. Soyez particulièrement prudent lorsque vous utilisez float et double pour des opérations exactes.

Analysons maintenant en détail pourquoi les opérations à virgule flottante entraînent une perte de précision ?



[java] view plain copy

Nous devons d'abord répondre aux deux questions suivantes :

(1) Comment convertir des entiers décimaux en nombres binaires

L'algorithme est très simple. Par exemple, 11 est exprimé sous forme de nombre binaire :

11/2=5 de plus de 1 5/2=2 de plus de 1

2/2 = 1 restant 0

1/2 = 0 余 1

0 fin La représentation binaire de 11 est (de bas en haut) : 1011

Une chose à mentionner ici : tant que le résultat après division est 0, c'est fini Pensez-y, tous les entiers divisés par 2 donneront certainement 0. En d’autres termes, l’algorithme qui convertit tous les entiers en nombres binaires continuera-t-il dans une boucle infinie ? Absolument pas. Les entiers peuvent toujours être représentés avec précision en binaire, mais les décimales ne le sont pas nécessairement.

(2) Comment convertir des nombres décimaux en nombres binaires

L'algorithme consiste à multiplier par 2 jusqu'à ce qu'il n'y ait plus de décimales. Par exemple, 0,9 est exprimé sous forme de nombre binaire

0,9*2=1,8 Prenez la partie entière 1

0,8 (partie fractionnaire de 1,8 )* 2=1.6 Obtenez la partie entière 1

0.6*2=1.2 Obtenez la partie entière 1

0.2*2=0.4 Prenez la partie entière 0

0,4*2=0,8 Prendre la partie entière 0

0,8*2=1,6 Prendre la partie entière 1

0,6*2 = 1,2 Prendre la partie entière 0

......... 0,9 binaire indiqué (de haut en bas) : 1100100100100 .....

Remarque : Le processus de calcul ci-dessus est une boucle, c'est-à-dire que*2 n'éliminera jamais la partie décimale, donc l'algorithme sera infini. Évidemment, la représentation binaire des décimales est parfois impossible à être précise. En fait, la raison est très simple. Peut-on représenter avec précision 1/3 dans le système décimal ? De même, le système binaire ne peut pas représenter avec précision 1/10. Cela explique également pourquoi la soustraction à virgule flottante pose le problème d'une perte de précision « inépuisable ».


3. Solution 1 :


Si cela ne vous dérange pas d'enregistrer vous-même le point décimal et que la valeur est pas grand, Ensuite, vous pouvez utiliser des types de base tels que long et int. Que vous utilisiez int ou long dépend de la taille de la plage numérique impliquée. L'inconvénient est que vous devez gérer vous-même la virgule décimale. utiliser des centimes pour calculer la monnaie au lieu du yuan (implique uniquement l'ajout d'une réduction).

Par exemple :

[java] view plain copy

int resultInt = 10 - 9

double result = (double) resultInt / 100; / /Au final, vous contrôlez vous-même le point décimal

4. Solution 2 :

Utilisez BigDecmal, et vous devez utiliser le type String dans les paramètres de construction.

Une solution est donnée dans le livre "Effective Java". Le livre souligne également que float et double ne peuvent être utilisés que pour des calculs scientifiques ou techniques. Dans des calculs précis tels que les calculs commerciaux, nous devons utiliser java.math.BigDecimal.

La classe BigDecimal a 4 méthodes. Nous ne nous soucions que des méthodes utiles pour des calculs précis de données à virgule flottante, c'est-à-dire

BigDecimal(double value) // Convertir des données doubles. En données de type BigDecimal

L'idée est très simple. Nous convertissons d'abord les données de type double en données BigDecimal via la méthode BigDecimal (valeur double), puis nous pouvons effectuer des calculs précis normalement. Une fois le calcul terminé, nous pouvons effectuer certains traitements sur les résultats, comme arrondir les résultats qui ne peuvent pas être divisés. Enfin, le résultat est reconverti des données BigDecimal en données doubles.

Cette idée est correcte, mais si vous regardez de plus près la description détaillée de BigDecimal dans l'API, vous saurez que si des calculs précis sont requis, nous ne pouvons pas utiliser double directement, mais devons utiliser String pour construire GrosDécimal. Par conséquent, nous commençons à nous soucier d'une autre méthode de la classe BigDecimal, à savoir la méthode BigDecimal(String value) qui peut nous aider à effectuer correctement des calculs précis.

// BigDecimal (String value) peut convertir les données String en données BigDecimal

Alors voici le problème, imaginez-le, si nous voulons faire une opération d'addition de données à virgule flottante, nous devons d'abord convertir les deux nombres à virgule flottante en données String, puis utiliser BigDecimal (valeur String) pour construire un BigDecimal , puis Pour appeler la méthode add sur l'un d'eux, passez l'autre en paramètre, puis convertissez le résultat de l'opération (BigDecimal) en un nombre à virgule flottante. Si vous devez faire cela à chaque fois que vous effectuez des calculs sur des données à virgule flottante, pouvez-vous supporter un processus aussi fastidieux ? Au moins, je ne peux pas. La meilleure façon est donc d’écrire une classe et d’effectuer ces processus de conversion fastidieux dans la classe. De cette façon, lorsque nous devons effectuer des calculs de données en virgule flottante, il nous suffit d'appeler cette classe. Certains experts sur Internet nous ont fourni un outil de classe Arith pour réaliser ces opérations de conversion. Il fournit les méthodes statiques suivantes, qui peuvent compléter les opérations d'addition, de soustraction, de multiplication et de division de données à virgule flottante et arrondir les résultats :

public static double add(double v1,double v2)
public double sous statique (double v1, double v2)
public statique double mul (double v1, double v2)
public statique double div (double v1, double v2)
public statique double div (double v1, double v2,int scale)
public static double round(double v, int scale)

Le code source d'Arith sera joint ci-dessous. Il vous suffit de le compiler et de le sauvegarder lorsque vous souhaitez effectuer un flottement. calculs de points, dans votre En important la classe Arith dans le programme source, vous pouvez utiliser les méthodes statiques ci-dessus pour effectuer des calculs précis de nombres à virgule flottante.

Annexe : Code source d'Arith

[java] afficher la copie simple

importer java.math.BigDecimal

/**

* Étant donné que les types simples de Java ne peuvent pas effectuer d'opérations précises sur les nombres à virgule flottante, cette classe utilitaire fournit des opérations

* précises sur les nombres à virgule flottante, notamment l'addition, la soustraction, la multiplication, la division et l'arrondi.

*/

classe publique Arith{

//Précision de l'opération de division par défaut

privé statique final int DEF_DIV_SCALE = 10;

//Cette classe ne peut pas être instanciée

private Arith(){

}

/**

* Fournit des opérations d'addition précises.

* @param v1 summand

* @param v2 addend

* @return somme de deux paramètres

*/ 

     double ajout statique public (double v1,double v2){                                                                                                                                          BigDecimal b2 = new BigDecimal(Double.toString(v2));

return b1.add(b2) .doubleValue();

} */

public static double sub(double v1,double v2){

BigDecimal b1 = new BigDecimal(Double.toString(v1) );

BigDecimal b2 = new BigDecimal(Double.toString(v2));

return b1.subtract(b2).doubleValue();

/**

* Fournit des opérations de soustraction précises.

* @param v1 Minuend

* @param v2 Minuend

* @return La différence entre les deux paramètres

*/

public static double mul(double v1,double v2){

BigDecimal b1 = new BigDecimal(Double.toString(v1)); 🎜>

BigDecimal b 2 = new BigDecimal(Double .toString(v2));

        return b1.multiply(b2).doubleValue();  

    }  

  

    /**

* Fournit des opérations de division (relativement) précises. Lorsque la division se produit, elle est précise à

* 10 décimales après la virgule, et les nombres suivants seront arrondis.

* @param v1 dividende

* @param v2 diviseur

* @return quotient des deux paramètres

*/  

    publique statique double div(double v1,double v2){  

        retourner div(v1,v2,DEF_DIV_SCALE);  

    }  

  

    /**

* Fournit des opérations de division (relativement) précises. Lorsqu'une division inépuisable se produit, la précision est spécifiée par le paramètre d'échelle

* et les nombres suivants sont arrondis.

* @param v1 dividend

* @param v2 diviseur

* L'échelle @param signifie qu'elle doit être précise à plusieurs décimales près.

* @return Le quotient des deux paramètres

*/  

    public static double div(double v1,double v2,int scale){  

        if(scale<0){  

             throw new IllegalArgumentException(  

                       " L'échelle doit être un entier positif ou zéro ");  

        }  

        BigDecimal b1 = new BigDecimal(Double.toString(v1));  

        BigDecimal b2 = new BigDecimal(Double.toString(v2));  

        return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();  

    }  

    /**

* Fournit un traitement précis de l'arrondi des décimales.

* @param v Le nombre à arrondir

* @param scale Combien de décimales conserver après la virgule

* @return Le résultat après arrondi

*/  

    public statique double tour (double v, échelle int){  

  

        if(scale<0){  

            throw new IllegalArgumentException(  

                "        " L'échelle doit être un entier positif ou zéro ");  

        }  

        BigDecimal b = new BigDecimal(Double.toString(v));  

        BigDecimal one = new BigDecimal("1");  

        return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();  

    }  

} ;  


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn