public class doublePrecision {
public static void main(String[] args) {
double total = 0;
total += 5.6;
total += 5.8;
System.out.println(total);
}
}
Le code ci-dessus est imprimé:
11.399999999999
Comment pourrais-je obtenir ceci pour simplement imprimer (ou pouvoir l'utiliser comme) 11.4?
Comme d'autres l'ont mentionné, vous voudrez probablement utiliser la classe BigDecimal
, si vous voulez avoir une représentation exacte de 11.4.
Maintenant, une petite explication sur la raison pour laquelle cela se produit:
Les types primitifs float
et double
dans Java sont virgule flottante , où le nombre est stocké sous la forme d'une représentation binaire d'un fraction et un exposant.
Plus spécifiquement, une valeur à virgule flottante double précision telle que le type double
est une valeur de 64 bits, où:
Ces parties sont combinées pour produire une représentation double
d'une valeur.
(Source: Wikipedia: Double precision )
Pour une description détaillée de la manière dont les valeurs en virgule flottante sont gérées en Java, voir le Section 4.2.3: Types de point flottant, formats et valeurs de Java Langage Spécification.
Les types byte
, char
, int
, long
sont point fixe , qui sont des représentations exactes des nombres. Contrairement aux nombres à virgule fixe, les nombres à virgule flottante seront parfois incapables de renvoyer une représentation exacte d'un nombre. C'est la raison pour laquelle vous vous retrouvez avec 11.399999999999
à la suite de 5.6 + 5.8
.
Lorsque vous avez besoin d'une valeur exacte, telle que 1.5 ou 150.1005, vous voudrez utiliser l'un des types à virgule fixe, qui pourra représenter le nombre avec précision.
Comme cela a déjà été mentionné à plusieurs reprises, Java a une classe BigDecimal
qui gérera de très grands nombres et de très petits nombres.
De la référence de l'API Java pour la classe BigDecimal
:
Nombres décimaux signés immuables de précision arbitraire. Un BigDecimal consiste en une valeur non mise à l’échelle avec un nombre entier de précision arbitraire et une échelle d’entier sur 32 bits. Si zéro ou positif, l'échelle correspond au nombre de chiffres à droite du point décimal. Si elle est négative, la valeur non échelonnée du nombre est multipliée par dix à la puissance de la négation de l'échelle. La valeur du nombre représenté par le BigDecimal est donc (unscaledValue × 10 ^ -scale).
Il y a eu beaucoup de questions sur le dépassement de pile concernant la question des nombres en virgule flottante et sa précision. Voici une liste de questions connexes pouvant présenter un intérêt:
Si vous voulez vraiment entrer dans les détails sérieux des nombres à virgule flottante, jetez un oeil à Ce que tout informaticien devrait savoir sur l'arithmétique en virgule flottante .
Lorsque vous entrez un nombre double, par exemple, 33.33333333333333
, la valeur que vous obtenez est en fait la valeur à double précision représentable la plus proche, qui est exactement:
33.3333333333333285963817615993320941925048828125
En divisant par 100, on obtient:
0.333333333333333285963817615993320941925048828125
qui n'est pas non plus représentable sous forme de nombre double précision, il est donc arrondi à la valeur représentable la plus proche, qui est exactement:
0.3333333333333332593184650249895639717578887939453125
Lorsque vous imprimez cette valeur, elle est arrondie encore une fois à 17 chiffres décimaux, donnant:
0.33333333333333326
Si vous souhaitez simplement traiter les valeurs sous forme de fractions, vous pouvez créer une classe de fraction contenant un champ de numérateur et de dénominateur.
Écrire des méthodes pour additionner, soustraire, multiplier et diviser ainsi qu’une méthode toDouble. De cette façon, vous pouvez éviter les flotteurs lors des calculs.
EDIT: mise en oeuvre rapide,
public class Fraction {
private int numerator;
private int denominator;
public Fraction(int n, int d){
numerator = n;
denominator = d;
}
public double toDouble(){
return ((double)numerator)/((double)denominator);
}
public static Fraction add(Fraction a, Fraction b){
if(a.denominator != b.denominator){
double aTop = b.denominator * a.numerator;
double bTop = a.denominator * b.numerator;
return new Fraction(aTop + bTop, a.denominator * b.denominator);
}
else{
return new Fraction(a.numerator + b.numerator, a.denominator);
}
}
public static Fraction divide(Fraction a, Fraction b){
return new Fraction(a.numerator * b.denominator, a.denominator * b.numerator);
}
public static Fraction multiply(Fraction a, Fraction b){
return new Fraction(a.numerator * b.numerator, a.denominator * b.denominator);
}
public static Fraction subtract(Fraction a, Fraction b){
if(a.denominator != b.denominator){
double aTop = b.denominator * a.numerator;
double bTop = a.denominator * b.numerator;
return new Fraction(aTop-bTop, a.denominator*b.denominator);
}
else{
return new Fraction(a.numerator - b.numerator, a.denominator);
}
}
}
Observez que vous auriez le même problème si vous utilisiez l'arithmétique décimale à précision limitée et que vous souhaitiez traiter avec 1/3: 0.333333333 * 3 égal à 0.999999999, et non à 1.00000000.
Malheureusement, 5.6, 5.8 et 11.4 ne sont tout simplement pas des nombres ronds en binaire, car ils impliquent des cinquièmes. Donc, leur représentation flottante n’est pas exacte, tout comme 0.3333 ne correspond pas exactement à 1/3.
Si tous les nombres que vous utilisez sont des nombres décimaux non récurrents et que vous voulez des résultats exacts, utilisez BigDecimal. Ou, comme d’autres l’ont déjà dit, si vos valeurs ressemblent à de l’argent en ce sens qu’elles sont toutes des multiples de 0,01, ou de 0,001, ou quelque chose comme cela, multipliez le tout par une puissance fixe de 10 et utilisez int ou long (addition et soustraction sont trivial: attention à la multiplication).
Cependant, si vous êtes satisfait du binaire pour le calcul, mais que vous voulez simplement imprimer les choses dans un format légèrement plus convivial, essayez Java.util.Formatter
ou String.format
. Dans la chaîne de formatage, spécifiez une précision inférieure à la précision complète d'un double. Par exemple, 11,399999999999 est égal à 11,4 pour 10 chiffres significatifs, de sorte que le résultat sera presque aussi précis et lisible par l'homme dans les cas où le résultat binaire est très proche d'une valeur ne nécessitant que quelques décimales.
La précision à spécifier dépend un peu du calcul que vous avez fait avec vos nombres - en général, plus vous en faites, plus l'erreur s'accumule, mais certains algorithmes l'accumulent beaucoup plus rapidement que d'autres (ils sont appelés "instable" opposé à "stable" en ce qui concerne les erreurs d’arrondi). Si vous ne faites que l'ajout de quelques valeurs, alors j'imagine que le fait de supprimer une précision d'une décimale suffit à régler le problème. Expérience.
Vous voudrez peut-être envisager d'utiliser la classe Java.math.BigDecimal de Java si vous avez vraiment besoin de mathématiques de précision. Voici un bon article d'Oracle/Sun sur le cas de BigDecimal . Bien que vous ne puissiez jamais représenter 1/3 comme quelqu'un l'a mentionné, vous pouvez avoir le pouvoir de décider avec quelle précision vous voulez que le résultat soit. setScale () est votre ami .. :)
Ok, car il me reste trop de temps, voici un exemple de code correspondant à votre question:
import Java.math.BigDecimal;
/**
* Created by a wonderful programmer known as:
* Vincent Stoessel
* [email protected]
* on Mar 17, 2010 at 11:05:16 PM
*/
public class BigUp {
public static void main(String[] args) {
BigDecimal first, second, result ;
first = new BigDecimal("33.33333333333333") ;
second = new BigDecimal("100") ;
result = first.divide(second);
System.out.println("result is " + result);
//will print : result is 0.3333333333333333
}
}
et pour brancher mon nouveau langage préféré, Groovy, voici un exemple plus net de la même chose:
import Java.math.BigDecimal
def first = new BigDecimal("33.33333333333333")
def second = new BigDecimal("100")
println "result is " + first/second // will print: result is 0.33333333333333
Vous ne pouvez pas, car 7.3 n'a pas de représentation finie en binaire. Le plus proche que vous puissiez obtenir est 2054767329987789/2 ** 48 = 7.3 + 1/1407374883553280.
Jetez un œil à http://docs.python.org/tutorial/floatingpoint.html pour une explication plus détaillée. (C’est sur le Python, mais Java et C++ ont le même "problème".)
La solution dépend de la nature exacte de votre problème:
Vous vous heurtez à la limite de précision du type double.
Java.Math possède des fonctionnalités arithmétiques de précision arbitraire.
Comme d'autres l'ont noté, toutes les valeurs décimales ne peuvent pas être représentées sous forme binaire, car elles sont basées sur des puissances de 10 et binaires sur des puissances de deux.
Si la précision compte, utilisez BigDecimal, mais si vous souhaitez simplement une sortie conviviale:
System.out.printf("%.2f\n", total);
Te donnera:
11.40
Je suis sûr que vous auriez pu en faire un exemple à trois lignes. :)
Si vous voulez une précision exacte, utilisez BigDecimal. Sinon, vous pouvez utiliser des nombres multipliés par 10, quelle que soit la précision souhaitée.
private void getRound() {
// this is very simple and interesting
double a = 5, b = 3, c;
c = a / b;
System.out.println(" round val is " + c);
// round val is : 1.6666666666666667
// if you want to only two precision point with double we
// can use formate option in String
// which takes 2 parameters one is formte specifier which
// shows dicimal places another double value
String s = String.format("%.2f", c);
double val = Double.parseDouble(s);
System.out.println(" val is :" + val);
// now out put will be : val is :1.67
}
Utilisez Java.math.BigDecimal
Les doubles étant des fractions binaires internes, ils ne peuvent parfois pas représenter les fractions décimales à la décimale exacte.
Les ordinateurs stockent les numéros en binaire et ne peuvent pas représenter des nombres tels que 33.333333333 ou 100.0 exactement. C'est l'une des choses les plus délicates à propos de l'utilisation des doubles. Vous devrez juste arrondir la réponse avant de la montrer à un utilisateur. Heureusement, dans la plupart des applications, vous n’avez de toute façon pas besoin de autant de décimales.
Les nombres en virgule flottante diffèrent des nombres réels en ce que, pour tout nombre donné, il existe un nombre immédiatement supérieur. Identique à des entiers. Il n'y a pas d'entier entre 1 et 2.
Il n'y a aucun moyen de représenter 1/3 en tant que float. Il y a un flotteur en dessous et un flotteur au-dessus, et il y a une certaine distance entre eux. Et 1/3 est dans cet espace.
Apfloat for Java prétend fonctionner avec des nombres à virgule flottante en précision arbitraire, mais je ne l'ai jamais utilisé. Vaut probablement le coup d'oeil. http://www.apfloat.org/apfloat_Java /
Une question similaire a été posée ici auparavant bibliothèque Java à haute précision en virgule flottante
Multipliez tout par 100 et stockez-le dans des centaines de centimes.
Les doubles sont approximations des nombres décimaux de votre source Java. Vous constatez la conséquence de l'inadéquation entre le double (qui est une valeur codée binaire) et votre source (qui est codée en décimal).
Java produit l'approximation binaire la plus proche. Vous pouvez utiliser Java.text.DecimalFormat pour afficher une valeur décimale plus esthétique.
Utilisez un BigDecimal. Il vous permet même de spécifier des règles d’arrondi (comme ROUND_HALF_EVEN, qui minimisera les erreurs statistiques en arrondissant au voisin pair si les deux distances sont égales; c’est-à-dire que les deux 1,5 et 2,5 sont arrondis à 2).
Si vous n'avez pas d'autre choix que d'utiliser des valeurs doubles, vous pouvez utiliser le code ci-dessous.
public static double sumDouble(double value1, double value2) {
double sum = 0.0;
String value1Str = Double.toString(value1);
int decimalIndex = value1Str.indexOf(".");
int value1Precision = 0;
if (decimalIndex != -1) {
value1Precision = (value1Str.length() - 1) - decimalIndex;
}
String value2Str = Double.toString(value2);
decimalIndex = value2Str.indexOf(".");
int value2Precision = 0;
if (decimalIndex != -1) {
value2Precision = (value2Str.length() - 1) - decimalIndex;
}
int maxPrecision = value1Precision > value2Precision ? value1Precision : value2Precision;
sum = value1 + value2;
String s = String.format("%." + maxPrecision + "f", sum);
sum = Double.parseDouble(s);
return sum;
}
Découvrez BigDecimal, il gère les problèmes liés à l’arithmétique en virgule flottante.
Le nouvel appel ressemblerait à ceci:
term[number].coefficient.add(co);
Utilisez setScale () pour définir le nombre de précision de la décimale à utiliser.
Pourquoi ne pas utiliser la méthode round () de la classe Math?
// The number of 0s determines how many digits you want after the floating point
// (here one digit)
total = (double)Math.round(total * 10) / 10;
System.out.println(total); // prints 11.4