我目前正在为医疗设备开发固件,其中涉及许多困难的数学运算。目标处理器在硬件中支持浮点操作,但仅支持float32
(又名single
)。
为模拟行为并证明公式和代码的正确性,我已将固件的相关/数学部分移植到Linux中的GCC工具链(gcc 6.3.0,libc6 2.24)中,仔细检查了{{ 1}}随处可见,并且不使用编译器开关,这会降低数学运算的精度或标准兼容性;值得注意的是,float32
或其朋友都没有。
现在,事实证明,对于一小组输入参数,我得到了意外的结果。我已经找到了问题所在,并得出结论,-ffast-math
为一组很小的输入参数计算了libm
(准确地说是arctan
)的错误结果。>
例如,如果我有
atan2
#include <math.h>
#define C_RAD2DEG (57.29577951308f)
int main(void)
{
float f_Temp = C_RAD2DEG * atan2f(0.713114202f, 0.665558934f);
}
被计算为f_Temp
,正确的结果将是46.9755516f
。
请注意,我通常了解不同的浮点数据类型,舍入错误等问题。
但是,我的感觉是,即使46.975548972f
的精度较低,上面显示的误差也仍然高一个数量级,不幸的是,对于随后的计算,该误差太大了。
此外,该问题仅影响float32
函数的可能输入参数的很小一部分。
任何人都可以简短地解释一下这是atan2
中的错误还是仅仅是由于libm
的不精确性以及计算float32
所需的大量顺序操作?
答案 0 :(得分:4)
您报告为观察结果的数字46.9755516f
对应于float
值46.975551605224609375。
您报告为预期结果的数字46.975548972f
对应于float
值46.97554779052734375。
这些是相邻的float
值,这意味着它们相差1单位的最低精度(ULP)。 (它们的差为3.814697265625ee-06,这是float
有效位中最低有效位的值,当最高有效位的值为32时(与47附近的数字相同)。)这是最小的数量float
可以按此比例更改。
通常,数学库例程难以实现,并且没有人使用正确的舍入(四舍五入到最接近精确数学值的可表示数字)和已知的有限运行时间来实现所有这些例程。三角函数中一些ULP错误并不罕见。
即使您使用的libc代码提供了正确的舍入结果,将其从弧度转换为度也会引入两个舍入误差(将180 /π转换为可表示的值并乘以它)。期望最终结果是最接近理想数学结果的float
是不合理的;您应该期望出现几次ULP错误。