bison和avr-g++中双精度幂的精度



我正在使用bisonavr微控制器编写计算器,我有一个2双精度幂的分辨率问题。

在我的bison文件中,我将类型定义为
%define api.value.type {double}
%token NUMBER

,然后给出以下规则

expr: NUMBER
| expr '^' expr {$$ = pow($1, $3);}

和代码工作正常,除非当我试图计算2^8,给我255.9999作为答案,而不是256

查看问题是否与doublepow,我已经修改了代码如下:

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可以与ab一起工作,这两个变量具有与$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。没有必要在运行时进行计算。Gcc只是将其重写为double c = 256.0;

Gcc使用它运行的机器上的数学库或它自己的捆绑数学库来进行计算。这很可能是Gnu数学库,它优化了小整数幂的计算。

pow的另一个调用是在运行时计算的,因为编译器不知道$1$3将是什么。所以这个调用是用微控制器上的数学库完成的,这是非常不准确的。(它可能会像exp(8.0 * log(2.0))一样,引入一个小的舍入错误。)

一个可能的解决方案是编写自己的pow实现,当指数是整数时,它使用更精确的计算。

avr-gcc double默认为32位。对于64位double,您需要avr-gcc v10+,参见GCC发行说明

https://gcc.gnu.org/gcc-10/changes.html avr

https://gcc.gnu.org/wiki/avr-gcc Libf7

浮点数固有的舍入和精度问题仍然存在。

最新更新