[英]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: 需要注意的几点:
FLT_MIN
and FLT_MAX
is exactly representable as a float
FLT_MIN
和FLT_MAX
之间的每2的FLT_MIN
FLT_MAX
可以精确地表示为float
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.