简短的回答是浮点运算存在表示错误和舍入错误。“toString()
知道”表示错误,因此如果没有舍入错误,您将看不到它。但是,如果舍入误差太大,你会这样做。
解决方案是使用 BigDecimal 或四舍五入您的结果。
如果您使用 BigDecimal,它将显示您真正拥有的确切值。
double d = 0.3d;
double f = 0.1d;
System.out.println("d= " + new BigDecimal(d));
System.out.println("f= " + new BigDecimal(f));
System.out.println("d+f= " + new BigDecimal(d + f));
System.out.println("0.4= " + new BigDecimal(0.4));
System.out.println("d*f= " + new BigDecimal(d * f));
System.out.println("0.03= " + new BigDecimal(0.03));
System.out.println("d-f= " + new BigDecimal(d - f));
System.out.println("0.2= " + new BigDecimal(0.2));
System.out.println("d/f= " + new BigDecimal(d / f));
System.out.println("(d-f)*(d-f)= " + new BigDecimal((d - f) * (d - f)));
印刷
d= 0.299999999999999988897769753748434595763683319091796875
f= 0.1000000000000000055511151231257827021181583404541015625
d+f= 0.40000000000000002220446049250313080847263336181640625
0.4= 0.40000000000000002220446049250313080847263336181640625
d*f= 0.0299999999999999988897769753748434595763683319091796875
0.03= 0.0299999999999999988897769753748434595763683319091796875
d-f= 0.1999999999999999833466546306226518936455249786376953125
0.2= 0.200000000000000011102230246251565404236316680908203125
d/f= 2.999999999999999555910790149937383830547332763671875
(d-f)*(d-f)= 0.03999999999999999389377336456163902767002582550048828125
你会注意到它0.1
有点太大了,0.3
也有点太小了。这意味着当您将它们相加或相乘时,您会得到一个大约正确的数字。但是,如果您使用减法或除法,则错误会累积,并且您会得到一个与表示的数字相差太远的数字。
即您可以看到 0.1 和 0.3 产生与 0.4 相同的值,而 0.3 - 0.1 不会产生与 0.2 相同的值
顺便说一句,您可以在不使用 BigDecimal 的情况下舍入答案
System.out.printf("d-f= %.2f%n", d - f);
System.out.printf("d/f= %.2f%n", d / f);
System.out.printf("(d-f)*(d-f)= %.2f%n", (d - f) * (d - f));
印刷
d-f= 0.20
d/f= 3.00
(d-f)*(d-f)= 0.04
或者
System.out.println("d-f= " + roundTo6Places(d - f));
System.out.println("d/f= " + roundTo6Places(d / f));
System.out.println("(d-f)*(d-f)= " + roundTo6Places((d - f) * (d - f)));
public static double roundTo6Places(double d) {
return (long)(d * 1e6 + (d > 0 ? 0.5 : -0.5)) / 1e6;
}
印刷
System.out.println("d-f= " + roundTo6Places(d - f));
System.out.println("d/f= " + roundTo6Places(d / f));
System.out.println("(d-f)*(d-f)= " + roundTo6Places((d - f) * (d - f)));
舍入消除了舍入误差(只留下 toString 旨在处理的表示错误)
0.1前后可以表示的值可以计算为
double before_f = Double.longBitsToDouble(Double.doubleToLongBits(f) - 1);
System.out.println("The value before 0.1 is " + new BigDecimal(before_f) + " error= " + BigDecimal.valueOf(0.1).subtract(new BigDecimal(before_f)));
System.out.println("The value after 0.1 is " + new BigDecimal(f) + " error= " + new BigDecimal(f).subtract(BigDecimal.valueOf(0.1)));
印刷
The value before 0.1 is 0.09999999999999999167332731531132594682276248931884765625
error= 8.32667268468867405317723751068115234375E-18
The value after 0.1 is 0.1000000000000000055511151231257827021181583404541015625
error= 5.5511151231257827021181583404541015625E-18