Java 使用双打时,为什么不是';t(x/(y*z))与(x/y/z)相同吗?
这部分是学术性的,因为为了我的目的,我只需要四舍五入到小数点后两位;但我很想知道是什么导致了两个略有不同的结果 这是我编写的测试,旨在将其缩小到最简单的实现:Java 使用双打时,为什么不是';t(x/(y*z))与(x/y/z)相同吗?,java,double,rounding,double-precision,operator-precedence,Java,Double,Rounding,Double Precision,Operator Precedence,这部分是学术性的,因为为了我的目的,我只需要四舍五入到小数点后两位;但我很想知道是什么导致了两个略有不同的结果 这是我编写的测试,旨在将其缩小到最简单的实现: @Test public void shouldEqual() { double expected = 450.00d / (7d * 60); // 1.0714285714285714 double actual = 450.00d / 7d / 60; // 1.0714285714285716 asser
@Test
public void shouldEqual() {
double expected = 450.00d / (7d * 60); // 1.0714285714285714
double actual = 450.00d / 7d / 60; // 1.0714285714285716
assertThat(actual).isEqualTo(expected);
}
但该输出失败:
org.junit.ComparisonFailure:
Expected :1.0714285714285714
Actual :1.0714285714285716
有人能详细解释引擎盖下发生了什么导致1.000000000000000X
的值不同吗
我在答案中寻找的一些要点是:
精度损失在哪里?
首选哪种方法,为什么?
哪一个是正确的?(在纯数学中,两者不可能都对。也许两者都错了?)
对于这些算术运算,有更好的解决方案或方法吗?当然,运算的顺序与双精度不精确的事实混杂在一起:
450.00d / (7d * 60) --> a = 7d * 60 --> result = 450.00d / a
vs
这是因为双除法通常会导致精度下降。所述损失可根据分割顺序而变化 当您除以
7d
时,实际结果已经失去了一些精度。然后,只有将错误结果除以60
当您除以7d*60
时,只需使用一次除法,因此仅会损失一次精度
请注意,双倍乘法有时也会失败,但这种情况不太常见。这与如何实现
double
类型以及浮点类型不能保证与其他更简单的数字类型相同的精度有关。虽然下面的答案更具体地说是关于求和的,但它也通过解释浮点数学运算中如何不保证无限精度来回答您的问题:。基本上,如果没有指定可接受的误差范围,就不应该尝试确定浮点值的相等性。谷歌的Guava库包括在一定精度内确定两个double
值的相等性。如果你想了解浮点等式的细节;同一地点。总而言之:由于运算顺序不同,计算结果的四舍五入不同,因此计算结果的预期值和实际值不同 我看到了一大堆问题,告诉你如何解决这个问题,但除了“浮点舍入错误很糟糕,m'kay?”之外,没有一个问题能够真正解释发生了什么,所以让我来试一试。首先让我指出,这个答案中没有任何东西是特定于Java的。舍入误差是任何数字的固定精度表示法所固有的问题,因此在C中也会遇到同样的问题
十进制数据类型中的舍入错误
作为一个简化的例子,假设我们有某种计算机,它本机使用无符号十进制数据类型,我们称之为float6d
。数据类型的长度为6位:4位专用于尾数,2位专用于指数。例如,数字3.142可以表示为
3.142 x 10^0
它将以6位数字存储,如下所示:
503142
前两位是指数加50,后四位是尾数。此数据类型可以表示从0.001 x 10^-50
到9.999 x 10^+49
的任何数字
事实上,那不是真的。它不能存储任何数字。如果你想代表3.141592怎么办?还是3.1412034?还是3.141488906?不幸的是,数据类型不能存储超过四位数的精度,因此编译器必须用更多位数舍入任何内容,以适应数据类型的约束。如果你写信
float6d x = 3.141592;
float6d y = 3.1412034;
float6d z = 3.141488906;
然后编译器将这三个值中的每一个转换为相同的内部表示形式,3.142 x 10^0
(记住,它存储为503142
),这样x==y==z
将保持为真
关键是,有一个完整的实数范围,所有这些实数都映射到相同的底层数字序列(或位,在真实的计算机中)。具体来说,任何满足
3.1415的x
让我们简化一下。您想知道的是为什么450d/420
和450d/7/60
(特别是)会给出不同的结果
让我们看看如何在IEE双精度浮点格式中执行除法。在不深入讨论实现细节的情况下,它基本上是对符号位进行异或运算,从被除数的指数中减去除数的指数,除以尾数,然后对结果进行规范化
首先,我们应该用适当的格式表示双精度的数字:
450 is 0 10000000111 1100001000000000000000000000000000000000000000000000
420 is 0 10000000111 1010010000000000000000000000000000000000000000000000
7 is 0 10000000001 1100000000000000000000000000000000000000000000000000
60 is 0 10000000100 1110000000000000000000000000000000000000000000000000
让我们首先将450
除以420
首先是符号位,它是0
(0 xor 0==0
)
然后是指数<代码>1000000111B-1000000111B+1023==1000000111B-1000000111B+011111B==01111111B
看起来不错,现在是尾数:
1.1100001000000000000000000000000000000000000000/1.1010010000000000000000000000000000000000000000==1.1100001/1.101001
。有两种不同的方法可以做到这一点,我将在稍后讨论它们。结果是1.0(001)
(您可以验证它)
现在我们应该使结果正常化。让我们看看guard、round和Stick位值:
000100100100001 0 0 1
防护位为0,我们不进行任何舍入。结果是,以二进制形式:
0 011111111 000100100001001001001
以十进制表示为1.0714285714285714
现在让我们通过类比将450
除以7
符号位=0
指数=100000011b-100000001b+011111111b==-011111001b+011111111b+011111111111b==100000011b
尾数=1.1100001/1.11==1.00000(001
1.210 x 10^2
+ 0.02718 x 10^2
-------------------
1.23718 x 10^2
1.210 x 10^2
x 0.02718 x 10^2
-------------------
3.28878 x 10^2
1.40 x 10^2
x 0.23 x 10^2
-------------------
3.22 x 10^2
3. x 10^0
/ 7. x 10^0
----------------------------
0.428571428571... x 10^0
sqrt(0.0001^2 + 0.0001^2) = 0.0001414
sqrt(0.0001^2 + 0.0001414^2) = 0.0001732
sqrt(0.0001414^2 + 0.0001^2) = 0.0001732
1.1100000000000000000000000000000000000000000000000000 x 2^00000000010
52 bits 11 bits
01000000000111100000000000000000000000000000000000000000000000000
^ ^
exponent mantissa
1.1100001000000000000000000000000000000000000000000000 x 2^00000001000
1.1110000000000000000000000000000000000000000000000000 x 2^00000000101
1.1010010000000000000000000000000000000000000000000000 x 2^00000001000
1.0001001001001001001001001001001001001001001001001001001001001001001001...
1.0001001001001001001001001001001001001001001001001001 x 2^00000000000
1000000.01001001001001001001001001001001001001001001001001001001001001001...
1.0000000100100100100100100100100100100100100100100101 x 2^00000000110
1.000100100100100100100100100100100100100100100100100110011001100110011...
1.0001001001001001001001001001001001001001001001001010 x 2^00000000000
1.0001001001001001001001001001001001001001001001001001001001001001001001...
1.0001001001001001001001001001001001001001001001001001100110011001100110...
^ last bit of mantissa
450 is 0 10000000111 1100001000000000000000000000000000000000000000000000
420 is 0 10000000111 1010010000000000000000000000000000000000000000000000
7 is 0 10000000001 1100000000000000000000000000000000000000000000000000
60 is 0 10000000100 1110000000000000000000000000000000000000000000000000
0.1000100100100100100100100100100100100100100100100100 1 1 0 0
1.0001001001001001001001001001001001001001001001001001 1 0 0