使用glibc / libm和float32

时间:2019-02-18 10:57:34

标签: c precision libc libm gcc6

我目前正在为医疗设备开发固件,其中涉及许多困难的数学运算。目标处理器在硬件中支持浮点操作,但仅支持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所需的大量顺序操作?

1 个答案:

答案 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错误。