简体   繁体   English

Arduino-pow(x,y)函数的奇指数行为

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

I've spent the past few hours debugging this problem, and eventually solved it myself. 我花了几个小时来调试此问题,并最终自己解决了。 Thought I'd post it here to prevent others from experiencing the same ridiculous problem. 以为我会在这里发布它,以防止其他人遇到同样可笑的问题。

I would definitely be open to a deeper explanation as to why my answer is an explanation. 对于我的回答为何是解释,我绝对可以接受更深入的解释。


I've been working with the pow(x,y) function to return exponents. 我一直在使用pow(x,y)函数来返回指数。 I was noticing very weird behavior with the exponents, and I can't quite understand why. 我注意到这些指数的行为很怪异,而我却不太明白为什么。 Here's my code: 这是我的代码:

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

And here's my output: 这是我的输出:

0 1
1 2
2 3
3 7
4 15

So these numbers are obviously not right. 因此,这些数字显然是不正确的。 Weird thing is, when I run the same code in a C++ program in Xcode (with cout statements instead of Serial output) I get the following (which I would expect): 奇怪的是,当我在Xcode的C ++程序中运行相同的代码(使用cout语句而不是Serial输出)时,得到以下内容(这是我期望的):

0 1
1 2
2 4
3 8
4 16

Why in the world would this return my expected values in Xcode but not on the arduino? 为什么这会在Xcode中返回我的期望值,而不是在arduino上返回? Why does the arduino return pow(2,n) = 2^n-1 for any value n larger than 1? 为什么arduino对于大于pow(2,n) = 2^n-1的任何值n返回pow(2,n) = 2^n-1 1?

Since the AVR doesn't have a FPU, pow() in avr-libc is implemented via calls to log() and exp() . 由于AVR没有FPU,因此avr-libc中的pow()是通过调用log()exp() Again, due to the lack of FPU, avr-libc uses approximations for both of those functions. 同样,由于缺少FPU,avr-libc对这两个函数都使用了近似值 This results in values that will be slightly off from the true value, which when cast to an integer can lose the least significant digit. 这将导致其值与真实值略有出入,将其转换为整数可能会丢失最低有效数字。

This doesn't happen on x86-class systems since those have hardware FPUs which are capable of giving the true integral value for non-negative powers of positive integers. 在x86级系统上不会发生这种情况,因为那些系统具有硬件FPU,它们能够为正整数的非负幂提供真实的整数值。

My suggestion is that if all you need is non-negative integral powers of integers then you should perform a series of bitwise shifts and adds rather than having to link in the non-trivial, non-exact libm . 我的建议是,如果您所需要的只是整数的非负整数幂,则应该执行一系列按位移位并加法,而不是必须链接到非平凡,非精确的libm

Ah, you juvenile fool, Ryan! 啊,你这少年傻瓜,瑞安! Have you no understanding of data types!? 您不了解数据类型!!

The Arduino pow() reference explicitly states that these values must be passed as floats and returned as doubles! Arduino pow()参考明确指出,这些值必须以浮点数形式传递,并以双精度形式返回! So let's use some brain cells and at least try returning a double! 因此,让我们使用一些脑细胞,至少尝试返回两倍!

Here's some code to highlight the craziness that's going on: 这是一些代码来突出说明正在发生的疯狂行为:

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
}

And here's your output: 这是您的输出:

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

Anyway, that will solve your problem. 无论如何,那将解决您的问题。 Casting the number as an int shows that it gets rounded down. 将数字强制转换为int表示将四舍五入。


Now, why does this happen? 现在,为什么会这样? Not exactly sure. 不确定。

Just as a complement to Ignacio Vazquez-Abrams' answer (which is the right answer), I wrote the following program to test for the accuracy of pow(2, i) for positive integer values of i : 正如补充伊格纳西奥巴斯克斯-艾布拉姆斯答案(这是正确的答案),我写了下面的程序来测试的准确性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;
}

On my PC (gcc 5.4.0 / Ubuntu 16.04), this program reports zero errors. 在我的PC(gcc 5.4.0 / Ubuntu 16.04)上,该程序报告零错误。 Running the same program (with proper stdio setup) on an Arduino Uno (avr-gcc 4.9.2 / avr-libc 1.8.0), I get errors as big as 90 ULPs ! 在Arduino Uno(avr-gcc 4.9.2 / avr-libc 1.8.0)上运行相同的程序(具有正确的stdio设置),我得到的错误多达 90个ULP Here is the output from the Uno: 这是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

A few points worth noting: 需要注意的几点:

  • every power of two between FLT_MIN and FLT_MAX is exactly representable as a float FLT_MINFLT_MAX之间的每2的FLT_MIN FLT_MAX可以精确地表示为float
  • the IEEE-754 standard requires the operations +, −, ×, ÷ and √ to be correctly rounded, which implies that computing 2 i by iteratively multiplying by 2 is guaranteed to give the exact result IEEE-754标准要求正确地对+,-,×,÷和√进行四舍五入,这意味着通过迭代乘以2来计算2 i可以保证得到准确的结果
  • the same standard does not require pow to be correctly rounded. 同样的标准并不要求pow被正确舍入。

Old question, but for future askers: 旧问题,但对于未来的提问者:

The reason 16.0 is rounded down to 15, is that the conversion from float to int is always by truncating the decimals. 16.0向下舍入为15的原因是,从float到int的转换始终是通过截断小数点来完成的。 So 15.99999 still becomes 15 and not 16. 因此15.99999仍然变为15,而不是16。

Since floating point values cannot precisely contain 16.0, they hold something like 15.99963513 (random example) which becomes 15 upon casting to an integer. 由于浮点值不能精确包含16.0,因此它们具有15.99963513之类的值(随机示例),该值在转换为整数时变为15。

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

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