bison 和 avr-g++ 中 double 的精度

Precision of a power of double in bison and avr-g++

我正在使用 bisonavr 微控制器编写计算器,但我遇到了 2 倍的幂的分辨率问题。

在我的 bison 文件中,我将类型定义为

    %define api.value.type {double}
    %token NUMBER

然后给出如下规则

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

并且代码正常工作,除非我尝试计算 2^8 时给出 255.9999 作为答案而不是 256

要查看问题出在 double 还是 pow 我已经这样修改了代码:

expr: NUMBER
     | expr '^' expr {$$ = pow(, );
                      double a = 2.0; double b = 8.0;
                      if (a == ) lcd << "a ok";  // prints ok
                      if (b == ) 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 一起使用,并且这两个变量与 </code> 和 [=27= 具有相同的值] 但 <code>$$ 不同于 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(, );}

它给了我错误的答案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 使用其 运行ning 机器上的数学库或使用其自己的捆绑数学库进行计算。这很可能是 Gnu 数学库,它优化了小整数幂的计算。

pow 的另一个调用是在 运行 时计算的,因为编译器不知道 </code> 和 <code> 会是什么。所以这个调用是用微控制器上的数学库完成的,这是非常不准确的。 (它可能会做类似 exp(8.0 * log(2.0)) 的事情,这会引入一个小的舍入误差。)

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

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

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

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

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