bison 和 avr-g++ 中 double 的精度
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(, );}
并且代码正常工作,除非我尝试计算 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
可以与 a
和 b
一起使用,并且这两个变量与 </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
不过,浮点数固有的舍入和精度问题仍然存在。
我正在使用 bison
为 avr
微控制器编写计算器,但我遇到了 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
可以与 a
和 b
一起使用,并且这两个变量与 </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
不过,浮点数固有的舍入和精度问题仍然存在。