为什么使用add2
方法的结果是错误的?Double.toString(d1)
为什么能保持double
的精度不丢失?
public static double add(double d1, double d2) {
BigDecimal bd1 = new BigDecimal(Double.toString(d1));
BigDecimal bd2 = new BigDecimal(Double.toString(d2));
return bd1.add(bd2).doubleValue();
}
public static double add2(double d1, double d2) {
BigDecimal bd1 = new BigDecimal(d1);
BigDecimal bd2 = new BigDecimal(d2);
return bd1.add(bd2).doubleValue();
}
public static void main(String[] args) {
double d1 = 1.0000002;
double d2 = 0.0000002;
System.out.println(Double.toString(d1));
System.out.println("d1 + d2 = " + (d1 + d2)); // 1.0000003999999998
System.out.println("d1 + d2 = " + add(d1, d2)); // 1.0000004
System.out.println("d1 + d2 = " + add2(d1, d2)); // 1.0000003999999998
}
PS:引出一个概念问题:
double d = XXXX.XXXXXX;
System.out.println(d); //这里总是不会丢失精度的,为什么?
Lorsque BigDecimal utilise double comme paramètre d'entrée, le binaire ne peut pas représenter avec précision les décimales. Une fois que le compilateur a lu les chaînes "0.0000002" et "1.0000002", il doit les convertir en une valeur double de 8 octets. C'est
1.0000001999999998947288304407265968620777130126953125
quelque chose comme ça. .Ainsi, lors de l'exécution, la valeur réelle réellement transmise au constructeur BigDecimal est
1.0000001999999998947288304407265968620777130126953125
.BigDecimal peut convertir correctement la chaîne en un nombre à virgule flottante vraiment précis lors de l'utilisation de String comme paramètre d'entrée.
Dans la partie System.out.println, si le paramètre d'entrée est une chaîne, il sera directement sorti. Si le paramètre d'entrée est d'un autre type, la méthode Object.toString sera appelée pour la conversion puis sortie. Double.toString utilisera une certaine précision pour arrondir le double avant de le sortir.
Le commentaire sur le constructeur BigDecimal résout ce problème :
Explication supplémentaire :
Double.toString
Cette méthode génère unString
et sera arrondie. Le paramètre d'entréenew BigDecimal(Double.toString(d1))
est unString
après traitement, et le constructeurBigDecimal(String val)
est appelé.La méthode BigDecimal(String val) dans le code source traitera val dans un tableau char[] :
Appelez ensuite le constructeur
BigDecimal(char[] in)
.Et les nouveaux appels BigDecimal(d1)
.BigDecimal(double val)
La première chose après l'arrivée de cette méthode estConvertissez les paramètres d'entrée en binaire, la précision sera donc perdue.
La perte de précision causée par la double addition est la même que la situation ci-dessus. Elle est d'abord convertie en bits puis calculée, puis la précision est perdue. .