[英]Precision of a power of double in bison and avr-g++
我正在使用bison
为avr
微控制器编写计算器,但我对 2 次幂的分辨率有问题。
在我的bison
文件中,我将类型定义为
%define api.value.type {double}
%token NUMBER
然后给出以下规则
expr: NUMBER
| expr '^' expr {$$ = pow($1, $3);}
并且代码正常工作,除非我尝试计算2^8
给出的答案是255.9999
而不是256
。
要查看问题出在double
还是pow
我已经这样修改了代码:
expr: NUMBER
| expr '^' expr {$$ = pow($1, $3);
double a = 2.0; double b = 8.0;
if (a == $1) lcd << "a ok"; // prints ok
if (b == $3) lcd << "b ok"; // prints ok
double c = pow(a, b);
lcd << c; // it shows 256!!!
if ($$ == c) lcd << "$$ ok";
else lcd << "$$ wrong"; // prints wrong!!!!
}
如您所见,函数pow
与a
和b
一起工作正常,这两个变量的值与$1
和$3
相同,但$$
与c = pow(a, b)
。
我不知道发生了什么。
这是我第一次使用bison
所以很可能我做错了什么。
我正在使用 avr-g++ 9.2.0 进行编译。
谢谢。
编辑:为了查看发生了什么,我以两种不同的方式修改了我的规则:
如果我尝试:
expr: NUMBER
| expr '^' expr {yyval = pow(2.0, 8.0);}
它给了我正确的答案并打印256
。
但是,如果我尝试:
expr: NUMBER
| expr '^' expr {yyval = pow($1, $3);}
它给了我错误的答案255.9999
这与野牛无关。 罪魁祸首是 AVR 微控制器上的数学库。
当你写(在 C 中):
double a = 2.0;
double b = 8.0;
double c = pow(a, b);
Gcc 足够聪明,可以确定 c 将是 256.0.0。 没有必要在运行时进行该计算。 Gcc 只是将其重写为double c = 256.0;
.
Gcc 使用它运行的机器上的数学库或使用它自己的捆绑数学库来进行计算。 这很可能是 Gnu 数学库,它优化了小整数幂的计算。
对pow
的另一个调用是在运行时计算的,因为编译器不知道$1
和$3
将是什么。 所以这个调用是通过微控制器上的数学库完成的,这有点不准确。 (它可能会做类似exp(8.0 * log(2.0))
事情,这会引入一个小的舍入误差。)
一种可能的解决方案是编写您自己的pow
实现,当指数为整数时,它使用更精确的计算。
avr-gcc double 默认为 32 位。 对于 64 位双精度,您需要 avr-gcc v10+,参见。 GCC 发行说明
https://gcc.gnu.org/gcc-10/changes.html#avr
https://gcc.gnu.org/wiki/avr-gcc#Libf7
不过,浮点数固有的舍入和精度问题仍然存在。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.