簡體   English   中英

Arduino-pow(x,y)函數的奇指數行為

[英]Arduino - odd exponent behavior with pow(x,y) function

我花了幾個小時來調試此問題,並最終自己解決了。 以為我會在這里發布它,以防止其他人遇到同樣可笑的問題。

對於我的回答為何是解釋,我絕對可以接受更深入的解釋。


我一直在使用pow(x,y)函數來返回指數。 我注意到這些指數的行為很怪異,而我卻不太明白為什么。 這是我的代碼:

for (int n=0;n<5;n++)
{
    int x = pow(2,n);
    Serial.print(n); 
    Serial.print(" ");
    Serial.println(x);
}

這是我的輸出:

0 1
1 2
2 3
3 7
4 15

因此,這些數字顯然是不正確的。 奇怪的是,當我在Xcode的C ++程序中運行相同的代碼(使用cout語句而不是Serial輸出)時,得到以下內容(這是我期望的):

0 1
1 2
2 4
3 8
4 16

為什么這會在Xcode中返回我的期望值,而不是在arduino上返回? 為什么arduino對於大於pow(2,n) = 2^n-1的任何值n返回pow(2,n) = 2^n-1 1?

由於AVR沒有FPU,因此avr-libc中的pow()是通過調用log()exp() 同樣,由於缺少FPU,avr-libc對這兩個函數都使用了近似值 這將導致其值與真實值略有出入,將其轉換為整數可能會丟失最低有效數字。

在x86級系統上不會發生這種情況,因為那些系統具有硬件FPU,它們能夠為正整數的非負冪提供真實的整數值。

我的建議是,如果您所需要的只是整數的非負整數冪,則應該執行一系列按位移位並加法,而不是必須鏈接到非平凡,非精確的libm

啊,你這少年傻瓜,瑞安! 您不了解數據類型!!

Arduino pow()參考明確指出,這些值必須以浮點數形式傳遞,並以雙精度形式返回! 因此,讓我們使用一些腦細胞,至少嘗試返回兩倍!

這是一些代碼來突出說明正在發生的瘋狂行為:

for (int n=0;n<5;n++)
{
    double x = pow(2,n);
    Serial.print(n); 
    Serial.print(" ");
    Serial.print(x);
    Serial.print(" ");
    Serial.println((int)x); // cast as int here
}

這是您的輸出:

0 1.00 1
1 2.00 2
2 4.00 3
3 8.00 7
4 16.00 15

無論如何,那將解決您的問題。 將數字強制轉換為int表示將四舍五入。


現在,為什么會這樣? 不確定。

正如補充伊格納西奧巴斯克斯-艾布拉姆斯答案(這是正確的答案),我寫了下面的程序來測試的准確性pow(2, i)為正整數i

#include <stdio.h>
#include <math.h>
#include <float.h>
#include <inttypes.h>

int main(void)
{
    printf("  i          2^i     correct   pow(2, i)         error  ulps\n");
    printf("------------------------------------------------------------\n");
    union { float f; uint32_t i; } x, y;
    x.f = 1;                    // 2^i, correct value
    float ulp_r = FLT_EPSILON;  // ULP to the right of x
    for (int i = 0; i < 128; i++, x.f *= 2, ulp_r *= 2) {
        y.f = pow(2, i);
        float error = y.f - x.f;
        float ulp = error < 0 ? ulp_r/2 : ulp_r;  // ULP(x - error)
        printf("%3d  %11.6g  0x%08"PRIx32"  0x%08"PRIx32"  %12.6g  %4g\n",
                i, x.f, x.i, y.i, error, error/ulp);
    }
    return 0;
}

在我的PC(gcc 5.4.0 / Ubuntu 16.04)上,該程序報告零錯誤。 在Arduino Uno(avr-gcc 4.9.2 / avr-libc 1.8.0)上運行相同的程序(具有正確的stdio設置),我得到的錯誤多達 90個ULP 這是Uno的輸出:

  i          2^i     correct   pow(2, i)         error  ulps
------------------------------------------------------------
  0            1  0x3f800000  0x3f800000             0     0
  1            2  0x40000000  0x40000000             0     0
  2            4  0x40800000  0x407ffffe  -4.76837e-07    -2
  3            8  0x41000000  0x40fffffc  -1.90735e-06    -4
  4           16  0x41800000  0x417ffffc   -3.8147e-06    -4
  5           32  0x42000000  0x41fffffa  -1.14441e-05    -6
  6           64  0x42800000  0x427ffffa  -2.28882e-05    -6
  7          128  0x43000000  0x42fffffa  -4.57764e-05    -6
  8          256  0x43800000  0x437ffffa  -9.15527e-05    -6
  9          512  0x44000000  0x43fffff4  -0.000366211   -12
 10         1024  0x44800000  0x447ffff4  -0.000732422   -12
 11         2048  0x45000000  0x44fffff4   -0.00146484   -12
 12         4096  0x45800000  0x457ffff4   -0.00292969   -12
 13         8192  0x46000000  0x46000000             0     0
 14        16384  0x46800000  0x467ffff4    -0.0117188   -12
 15        32768  0x47000000  0x46fffff4    -0.0234375   -12
 16        65536  0x47800000  0x477ffff4     -0.046875   -12
 17       131072  0x48000000  0x48000000             0     0
 18       262144  0x48800000  0x487fffea      -0.34375   -22
 19       524288  0x49000000  0x48ffffea       -0.6875   -22
 20  1.04858e+06  0x49800000  0x497fffea        -1.375   -22
 21  2.09715e+06  0x4a000000  0x4a000000             0     0
 22   4.1943e+06  0x4a800000  0x4a7fffea          -5.5   -22
 23  8.38861e+06  0x4b000000  0x4affffea           -11   -22
 24  1.67772e+07  0x4b800000  0x4b7fffea           -22   -22
 25  3.35544e+07  0x4c000000  0x4c000000             0     0
 26  6.71089e+07  0x4c800000  0x4c800000             0     0
 27  1.34218e+08  0x4d000000  0x4cffffea          -176   -22
 28  2.68435e+08  0x4d800000  0x4d7fffea          -352   -22
 29  5.36871e+08  0x4e000000  0x4e000000             0     0
 30  1.07374e+09  0x4e800000  0x4e7fffea         -1408   -22
 31  2.14748e+09  0x4f000000  0x4effffea         -2816   -22
 32  4.29497e+09  0x4f800000  0x4f7fffea         -5632   -22
 33  8.58993e+09  0x50000000  0x50000000             0     0
 34  1.71799e+10  0x50800000  0x50800000             0     0
 35  3.43597e+10  0x51000000  0x50ffffd2        -94208   -46
 36  6.87195e+10  0x51800000  0x517fffd2       -188416   -46
 37  1.37439e+11  0x52000000  0x52000000             0     0
 38  2.74878e+11  0x52800000  0x527fffd2       -753664   -46
 39  5.49756e+11  0x53000000  0x52ffffd2  -1.50733e+06   -46
 40  1.09951e+12  0x53800000  0x537fffd2  -3.01466e+06   -46
 41  2.19902e+12  0x54000000  0x54000000             0     0
 42  4.39805e+12  0x54800000  0x54800000             0     0
 43  8.79609e+12  0x55000000  0x54ffffd2  -2.41172e+07   -46
 44  1.75922e+13  0x55800000  0x557fffd2  -4.82345e+07   -46
 45  3.51844e+13  0x56000000  0x56000000             0     0
 46  7.03687e+13  0x56800000  0x567fffd2  -1.92938e+08   -46
 47  1.40737e+14  0x57000000  0x57000000             0     0
 48  2.81475e+14  0x57800000  0x577fffd2  -7.71752e+08   -46
 49   5.6295e+14  0x58000000  0x57ffffd2   -1.5435e+09   -46
 50   1.1259e+15  0x58800000  0x58800000             0     0
 51   2.2518e+15  0x59000000  0x58ffffd2  -6.17402e+09   -46
 52   4.5036e+15  0x59800000  0x59800000             0     0
 53   9.0072e+15  0x5a000000  0x5a000000             0     0
 54  1.80144e+16  0x5a800000  0x5a7fffd2  -4.93921e+10   -46
 55  3.60288e+16  0x5b000000  0x5b000000             0     0
 56  7.20576e+16  0x5b800000  0x5b7fffd2  -1.97568e+11   -46
 57  1.44115e+17  0x5c000000  0x5bffffd2  -3.95137e+11   -46
 58   2.8823e+17  0x5c800000  0x5c800000             0     0
 59  5.76461e+17  0x5d000000  0x5cffffd2  -1.58055e+12   -46
 60  1.15292e+18  0x5d800000  0x5d7fffd2   -3.1611e+12   -46
 61  2.30584e+18  0x5e000000  0x5e000000             0     0
 62  4.61169e+18  0x5e800000  0x5e7fffd2  -1.26444e+13   -46
 63  9.22337e+18  0x5f000000  0x5f000000             0     0
 64  1.84467e+19  0x5f800000  0x5f7fffd2  -5.05775e+13   -46
 65  3.68935e+19  0x60000000  0x5fffffa6  -1.97912e+14   -90
 66   7.3787e+19  0x60800000  0x60800000             0     0
 67  1.47574e+20  0x61000000  0x60ffffa6  -7.91648e+14   -90
 68  2.95148e+20  0x61800000  0x61800000             0     0
 69  5.90296e+20  0x62000000  0x61ffffa6  -3.16659e+15   -90
 70  1.18059e+21  0x62800000  0x627fffa6  -6.33319e+15   -90
 71  2.36118e+21  0x63000000  0x63000000             0     0
 72  4.72237e+21  0x63800000  0x637fffa6  -2.53327e+16   -90
 73  9.44473e+21  0x64000000  0x63ffffa6  -5.06655e+16   -90
 74  1.88895e+22  0x64800000  0x64800000             0     0
 75  3.77789e+22  0x65000000  0x64ffffa6  -2.02662e+17   -90
 76  7.55579e+22  0x65800000  0x657fffa6  -4.05324e+17   -90
 77  1.51116e+23  0x66000000  0x65ffffa6  -8.10648e+17   -90
 78  3.02231e+23  0x66800000  0x667fffa6   -1.6213e+18   -90
 79  6.04463e+23  0x67000000  0x67000000             0     0
 80  1.20893e+24  0x67800000  0x677fffa6  -6.48518e+18   -90
 81  2.41785e+24  0x68000000  0x67ffffa6  -1.29704e+19   -90
 82   4.8357e+24  0x68800000  0x68800000             0     0
 83  9.67141e+24  0x69000000  0x68ffffa6  -5.18815e+19   -90
 84  1.93428e+25  0x69800000  0x69800000             0     0
 85  3.86856e+25  0x6a000000  0x69ffffa6  -2.07526e+20   -90
 86  7.73713e+25  0x6a800000  0x6a7fffa6  -4.15052e+20   -90
 87  1.54743e+26  0x6b000000  0x6b000000             0     0
 88  3.09485e+26  0x6b800000  0x6b7fffa6  -1.66021e+21   -90
 89   6.1897e+26  0x6c000000  0x6bffffa6  -3.32041e+21   -90
 90  1.23794e+27  0x6c800000  0x6c800000             0     0
 91  2.47588e+27  0x6d000000  0x6cffffa6  -1.32817e+22   -90
 92  4.95176e+27  0x6d800000  0x6d7fffa6  -2.65633e+22   -90
 93  9.90352e+27  0x6e000000  0x6dffffa6  -5.31266e+22   -90
 94   1.9807e+28  0x6e800000  0x6e800000             0     0
 95  3.96141e+28  0x6f000000  0x6f000000             0     0
 96  7.92282e+28  0x6f800000  0x6f7fffa6  -4.25013e+23   -90
 97  1.58456e+29  0x70000000  0x6fffffa6  -8.50026e+23   -90
 98  3.16913e+29  0x70800000  0x707fffa6  -1.70005e+24   -90
 99  6.33825e+29  0x71000000  0x71000000             0     0
100  1.26765e+30  0x71800000  0x71800000             0     0
101   2.5353e+30  0x72000000  0x71ffffa6  -1.36004e+25   -90
102   5.0706e+30  0x72800000  0x727fffa6  -2.72008e+25   -90
103  1.01412e+31  0x73000000  0x72ffffa6  -5.44017e+25   -90
104  2.02824e+31  0x73800000  0x73800000             0     0
105  4.05648e+31  0x74000000  0x74000000             0     0
106  8.11296e+31  0x74800000  0x74800000             0     0
107  1.62259e+32  0x75000000  0x74ffffa6  -8.70427e+26   -90
108  3.24519e+32  0x75800000  0x757fffa6  -1.74085e+27   -90
109  6.49037e+32  0x76000000  0x75ffffa6  -3.48171e+27   -90
110  1.29807e+33  0x76800000  0x76800000             0     0
111  2.59615e+33  0x77000000  0x77000000             0     0
112   5.1923e+33  0x77800000  0x777fffa6  -2.78537e+28   -90
113  1.03846e+34  0x78000000  0x77ffffa6  -5.57073e+28   -90
114  2.07692e+34  0x78800000  0x787fffa6  -1.11415e+29   -90
115  4.15384e+34  0x79000000  0x79000000             0     0
116  8.30767e+34  0x79800000  0x79800000             0     0
117  1.66153e+35  0x7a000000  0x79ffffa6  -8.91317e+29   -90
118  3.32307e+35  0x7a800000  0x7a7fffa6  -1.78263e+30   -90
119  6.64614e+35  0x7b000000  0x7affffa6  -3.56527e+30   -90
120  1.32923e+36  0x7b800000  0x7b7fffa6  -7.13053e+30   -90
121  2.65846e+36  0x7c000000  0x7c000000             0     0
122  5.31691e+36  0x7c800000  0x7c800000             0     0
123  1.06338e+37  0x7d000000  0x7cffffa6  -5.70443e+31   -90
124  2.12676e+37  0x7d800000  0x7d7fffa6  -1.14089e+32   -90
125  4.25353e+37  0x7e000000  0x7dffffa6  -2.28177e+32   -90
126  8.50706e+37  0x7e800000  0x7e800000             0     0
127  1.70141e+38  0x7f000000  0x7f000000             0     0

需要注意的幾點:

  • FLT_MINFLT_MAX之間的每2的FLT_MIN FLT_MAX可以精確地表示為float
  • IEEE-754標准要求正確地對+,-,×,÷和√進行四舍五入,這意味着通過迭代乘以2來計算2 i可以保證得到准確的結果
  • 同樣的標准並不要求pow被正確舍入。

舊問題,但對於未來的提問者:

16.0向下舍入為15的原因是,從float到int的轉換始終是通過截斷小數點來完成的。 因此15.99999仍然變為15,而不是16。

由於浮點值不能精確包含16.0,因此它們具有15.99963513之類的值(隨機示例),該值在轉換為整數時變為15。

暫無
暫無

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

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