这个简单的“双倍”计算有什么问题?

2022-09-03 05:07:36

Java中这个简单的“双倍”计算有什么问题?

我知道有些十进制数不能用浮点数/双二进制格式正确表示,但是使用变量d3,java能够毫无问题地存储和显示2.64。

double d1 = 4.64;
double d2 = 2.0;
double d3 = 2.64;
double d4 = d1 - d2;

System.out.println("d1      : " + d1);
System.out.println("d2      : " + d2);
System.out.println("d3      : " + d3);
System.out.println("d4      : " + d4);
System.out.println("d1 - d2 : " + (d1 - d2));

d1      : 4.64
d2      : 2.0
d3      : 2.64
d4      : 2.6399999999999997
d1 - d2 : 2.6399999999999997

答案 1

问题

在二进制中,2.64 是 10.10100011110101110000101000111101 重复出现的,换句话说,在二进制中不能完全表示,因此误差很小。Java对d3很友善,但是一旦涉及实际计算,它必须回退到实际表示。

二进制计算器

更多:

2.64= 10.10100011110101110000101000111101
4.64=100.1010001111010111000010100011110 

现在,即使.64在这两种情况下是相同的,它也会保持不同的精度,因为4= 100比2 = 10消耗了更多的双倍有效数字,因此当您说4.64-2.0和2.64时,.64在两种情况下都表示不同的舍入误差,因此无法恢复这种丢失的信息以获得最终答案。

注意:贝我在这里没有使用有效数字的数量,只是二进制计算器会产生什么,但是无论有效数字的数量如何,效果都是相同的。double

永远不要假设双精度值是精确的(尽管它们的不准确性是微观的,并且只是因为某些数字不能用二进制精确表示而引起的)。


浮点数并不精确,但仅从小数点的角度来看

虽然你应该总是期望双精度值在最后几个小数位会有小错误,但认为二进制表示是“坏”或更差于小数是错误的。

我们都习惯于某些数字(例如1/3)不能完全用十进制表示,我们接受这样的数字最终将变为0.333333333333而不是真实值(如果没有无限空间,我无法写下来);正是在这种背景下,二进制数不能被精确地表示。1/10是一个不能用二进制精确表示的数字;这让我们感到担忧,只是因为我们习惯于十进制


答案 2

d1 - d2 返回二进制浮点算的确切结果,它是 2.63999999999999997,因此它被打印出来。如果要将其舍入,可以在打印过程中进行

System.out.printf("d1 - d2 : %.2f",  d4);

或与共享资源数学

d4 = Precision.round(d4, 2);

推荐