簡體   English   中英

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

[英]Floating-point equality unexpectedly working

我們經常被告知,不應該比較浮點數是否完全相等。 然而,下面的 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!
}

我認為它可能適用於某些輸入 arguments 但不適用於其他輸入。 但即使我使用 JQwik 的屬性測試,它仍然有效!

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

誰能告訴我為什么我從來沒有遇到過兩個浮點數相差很小的情況?

你很幸運,一般來說你不會得到完全平等。 試試這個例如:

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);
}

在您的具體示例中,必須仔細分析以證明您的迭代始終收斂到最接近 phi 的浮點數。 如果 sqrt 也返回最接近根的浮點數,我們將得到完全相等。

...令我驚訝的是它似乎總是有效:

總是

當我嘗試f(-1/1.6180339887498949)時, xy值在最后幾位不同的兩個浮點值之間振盪@Steve Summit 因此一個無限循環。

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)通常會收斂以呈現x ,使得1 + 1 / x x再次成為 x 並因此滿足停止條件。

更好的例程可以證明,如果x相當接近, while循環最終將接近所需的答案,但即便如此,如上所示的振盪也是可能的。 因此需要使用迭代限制足夠接近的測試 通常 2 個振盪值,當接近時,它們被按摩(例如平均)以形成最佳答案。 如果不關閉,則循環根本無法找到穩定的答案。


誰能告訴我為什么我從來沒有遇到過兩個浮點數相差很小的情況?

測試不充分。


故事的寓意

不要只依賴浮點數相等,select 情況除外。
f()不是 select 案例,應該有額外的停止代碼。


參考:具有數學屬性的兩個xx = 1 + 1/x

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

注意x1*x2 == -1 x1黃金比率 φ

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM