簡體   English   中英

(0.3)^ 3 ==(0.3)*(0.3)*(0.3)在matlab中返回false?

[英](0.3)^3 == (0.3)*(0.3)*(0.3) returns false in matlab?

我試圖理解MATLAB中基本算術運算的舍入誤差,我遇到了以下奇怪的例子。

(0.3)^3 == (0.3)*(0.3)*(0.3)

ans = 0

我想確切地知道如何計算左側。 MATLAB文檔表明,對於整數冪,使用“通過平方取冪”算法。

“矩陣功率。如果p是標量,則X ^ p是功率p的X.如果p是整數,則通過重復平方來計算功率。”

所以我假設(0.3)^3(0.3)*(0.3)^2將返回相同的值。 但這種情況並非如此。 如何解釋舍入誤差的差異?

我對MATLAB一無所知,但我在Ruby中嘗試過:

irb> 0.3 ** 3
  => 0.026999999999999996
irb> 0.3 * 0.3 * 0.3
  => 0.027

根據Ruby源代碼 ,如果左側操作數是浮點數,則取冪運算符將右側操作數轉換為浮點數,然后調用標准C函數pow() pow()函數的float變量必須實現更復雜的算法來處理非整數指數,這將使用導致舍入錯誤的操作。 也許MATLAB的工作方式類似。

有趣的是,標量^似乎是使用pow實現的,而矩陣^是使用square-and-multiply實現的。 以機智:

octave:13> format hex
octave:14> 0.3^3
ans = 3f9ba5e353f7ced8
octave:15> 0.3*0.3*0.3
ans = 3f9ba5e353f7ced9
octave:20> [0.3 0;0 0.3]^3
ans =

  3f9ba5e353f7ced9  0000000000000000
  0000000000000000  3f9ba5e353f7ced9

octave:21> [0.3 0;0 0.3] * [0.3 0;0 0.3] * [0.3 0;0 0.3]
ans =

  3f9ba5e353f7ced9  0000000000000000
  0000000000000000  3f9ba5e353f7ced9

這可以通過在gdb下運行octave並在pow設置斷點來確認。

在matlab中也是如此,但我無法真正驗證。

這是一個小測試程序,遵循Apple的Libm-2026 Source/Intel/xmm_power.c的系統pow() ,在這種情況下:

#include <stdio.h>
int main() {
    // basically lines 1130-1157 of xmm_power.c, modified a bit to remove
    // irrelevant things

    double x = .3;
    int i = 3;

    //calculate ix = f**i
    long double ix = 1.0, lx = (long double) x;

    //calculate x**i by doing lots of multiplication
    int mask = 1;

    //for each of the bits set in i, multiply ix by x**(2**bit_position)
    while(i != 0)
    {
        if( i & mask )
        {
            ix *= lx;
            i -= mask;
        }
        mask += mask;
        lx *= lx; // In double this might overflow spuriously, but not in long double
    }

    printf("%.40f\n", (double) ix);
}

這打印出0.0269999999999999962252417162744677625597 ,這與我在Matlab中得到的.3 ^ 3和Python中的.3 ** 3的結果一致(我們知道后者只是調用此代碼 )。 相比之下,對我來說.3 * .3 * .3得到0.0269999999999999996946886682280819513835 ,如果你只是要求將0.027打印到那么多的小數位,那么你可能得到同樣的東西,因此可能是最接近的雙倍。

所以有算法。 我們可以准確地找出每個步驟中設置的值,但是考慮到執行它的不同算法,它會轉向一個非常小的數字並不太令人驚訝。

感謝@Dougal我發現了這個:

#include <stdio.h>
int main() {
  double x = 0.3;
  printf("%.40f\n", (x*x*x));

  long double y = 0.3;
  printf("%.40f\n", (double)(y*y*y));
}

這使:

0.0269999999999999996946886682280819513835
0.0269999999999999962252417162744677625597

這種情況很奇怪,因為使用更多數字的計算會產生最差的結果。 這是因為無論如何,初始數字0.3用幾位數近似,因此我們從一個相對“大”的錯誤開始。 在這種特殊情況下,發生的事情是少數位的計算給出另一個“大”錯誤,但符號相反......因此補償了初始錯誤。 相反,使用更多數字的計算會產生第二個較小的錯誤,但第一個仍然存在。

閱讀Goldberg的“每個計算機科學家應該知道的浮點運算” (這是Oracle的重印)。 明白了。 浮點數不是微積分的實數。 對不起,沒有TL; DR版本可用。

暫無
暫無

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

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