简体   繁体   English

浮点相等性出乎意料地工作

[英]Floating-point equality unexpectedly working

We are often taught that floating-point numbers should not be compared for exact equality.我们经常被告知,不应该比较浮点数是否完全相等。 However, the following function, which returns the Golden Ratio when passed any positive number, does in fact compare doubles for equality and to my surprise it seems to always work:然而,下面的 function 在传递任何正数时返回黄金比例,实际上确实比较双精度数是否相等,令我惊讶的是它似乎总是有效:

public static double f(double x) {
    double y;
    while ((y = 1 + 1 / x) != x)
        x = (x + y) / 2;
    return x;
}

@Test
void test() {
    assertEquals((1 + sqrt(5)) / 2, f(1.0));  // Passes!
}

I thought that maybe it works for some input arguments but not others.我认为它可能适用于某些输入 arguments 但不适用于其他输入。 But even if I use JQwik's property testing, it still works!但即使我使用 JQwik 的属性测试,它仍然有效!

@Property
void test2(@ForAll @Positive double x) {
    assertEquals((1 + sqrt(5)) / 2, f(x));  // Always passes!
}

Can anyone tell me why I never come across a situation where the two floating-point numbers are different by a very small amount?谁能告诉我为什么我从来没有遇到过两个浮点数相差很小的情况?

You were just lucky, in general you don't get exact equality.你很幸运,一般来说你不会得到完全平等。 Try this for example:试试这个例如:

public static void main(String[] args) {
    var s = 0.0;
    for (int i = 0; i < 10; i++) {
        s += 0.1;
    }
    System.out.println(s == 1.0);
}

In your concrete example one would have to do a careful analysis to prove that your iteration always converges to the floating point number closest to phi.在您的具体示例中,必须仔细分析以证明您的迭代始终收敛到最接近 phi 的浮点数。 If sqrt also returns the closest floating point number to the exact root we would get exact equality.如果 sqrt 也返回最接近根的浮点数,我们将得到完全相等。

... and to my surprise it seems to always work: ...令我惊讶的是它似乎总是有效:

Not always .总是

When I tried f(-1/1.6180339887498949) , the x and y values oscillated between two floating point values that differed in the last few bits @Steve Summit .当我尝试f(-1/1.6180339887498949)时, xy值在最后几位不同的两个浮点值之间振荡@Steve Summit Thus an infinite loop.因此一个无限循环。

x:-0.61803398874989490  y:-0.61803398874989468   // Decimal notation
x:-0x1.3c6ef372fe950p-1 y:-0x1.3c6ef372fe94ep-1  // Hex notation

x:-0.61803398874989479  y:-0.6180339887498949
x:-0x1.3c6ef372fe94fp-1 y:-0x1.3c6ef372fe950p-1

x:-0.61803398874989490  y:-0.61803398874989468
x:-0x1.3c6ef372fe950p-1 y:-0x1.3c6ef372fe94ep-1

f(some_starting_x) generally converges to render an x , such that 1 + 1 / x is x again and so meeting the stopping condition. f(some_starting_x)通常会收敛以呈现x ,使得1 + 1 / x x再次成为 x 并因此满足停止条件。

Better routines can prove that if x is reasonably close, the while loop will eventually get close to the desired answer, yet even then, an oscillation, as shown above is possible.更好的例程可以证明,如果x相当接近, while循环最终将接近所需的答案,但即便如此,如上所示的振荡也是可能的。 Thus using an iteration limit or close enough test is needed.因此需要使用迭代限制足够接近的测试 Usually the 2 oscillation values, when close, they are massaged (eg averaged) to form the best answer.通常 2 个振荡值,当接近时,它们被按摩(例如平均)以形成最佳答案。 If not close, the looping simply failed to find a stable answer.如果不关闭,则循环根本无法找到稳定的答案。


Can anyone tell me why I never come across a situation where the two floating-point numbers are different by a very small amount?谁能告诉我为什么我从来没有遇到过两个浮点数相差很小的情况?

Inadequate testing.测试不充分。


Morale of the story :故事的寓意

Do not rely on only floating point equality, except in select cases.不要只依赖浮点数相等,select 情况除外。
f() was not a select case and deserved additional stopping code. f()不是 select 案例,应该有额外的停止代码。


Ref: Two x with math property: x = 1 + 1/x :参考:具有数学属性的两个xx = 1 + 1/x

x1 = 1.6180339887498948482045868343656...
x2 = -0.61803398874989484820458683436564...

Note x1*x2 == -1 .注意x1*x2 == -1 x1 is the Golden_ratio φ . x1黄金比率 φ

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM