0

我目前正在为涉及许多困难数学运算的医疗设备开发固件。目标处理器支持硬件中的浮点运算,但仅支持float32(aka single)。

为了模拟行为并证明我的公式和代码的正确性,我已将固件的相关/数学部分移植到 Linux 中的 GCC 工具链(gcc 6.3.0,libc6 2.24),仔细检查float32无处不在,并且没有使用会降低数学运算的精度或标准兼容性的编译器开关;值得注意的是,没有任何一个-ffast-math或它的朋友。

现在,事实证明,对于一小组输入参数,我得到了意想不到的结果。我已经找到了这个问题,并得出结论,为(准确地说:)为一组非常小的输入参数libm计算了错误的结果。arctanatan2

例如,如果我有

#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和需要大量的顺序操作atan2

4

1 回答 1

4

您作为观察结果报告的数字46.9755516f对应于float值 46.975551605224609375。

您作为预期结果报告的数字46.975548972f对应于float值 46.97554779052734375。

这些是相邻的float值,这意味着它们相差 1 个最小精度单位 (ULP)。(它们的区别是 3.814697265625e-06,float当最高有效位的值为 32 时,它是有效位中最低有效位的值,就像 47 左右的数字一样。)这是 afloat可以改变的最小可能量在那个规模。

一般来说,数学库例程很难实现,没有人用正确的舍入(舍入到最接近精确数学值的可表示数字)和已知的有界运行时间来实现所有这些例程。在三角函数例程中,一些 ULP 错误并不罕见。

即使您使用的 libc 代码提供了正确舍入的结果,将其从弧度转换为度数也会引入两个舍入误差(将 180/π 转换为可表示的值并乘以它)。float期望最终结果是最接近理想数学结果的结果是不合理的;你应该期待几个 ULP 的错误。

于 2019-02-18T13:10:24.587 回答