来自类似浮点函数的不同结果

Different results from similar floating-point functions

本文关键字:结果 函数      更新时间:2023-10-16

所以我有 2 个函数应该做同样的事情

float ver1(float a0, float a1) {
    float r0 = a0 - a1;
    if (abs(r0) > PI) {
        if (r0 > 0) {
            r0 -= PI2;
        } else {
            r0 += PI2;
            }
    }
    return r0;
}
float ver2(float a0, float a1) {
    float a2 = a1 - PI2;
    float r0 = a0 - a1;
    float r1 = a0 - a2;
    if (abs(r0) < abs(r1)) {
        return r0;
    }
    if (abs(r0) > abs(r1)) {
        return r1;
    }
    return 0;
}

注意:PI 和 PI2 是 pi 和 2*pi 的浮点常数

奇怪的是,

有时它们会产生不同的结果,例如,如果您喂它们 0.28605145 和 5.9433694,那么第一个结果是 0.62586737,第二个结果是 0.62586755,我无法弄清楚是什么导致了这种情况。

如果你手动计算结果应该是什么,你会发现第二个答案是正确的。我在 2D 物理模拟中使用这个功能,真正奇怪的是,第一个答案(错误的答案)在那里工作,而第二个答案(正确的答案)让它表现得非常疯狂。来自未知来源的如此微小的差异和如此深远的影响:|

在这一点上,无论如何我都会切换到矩阵,但这种奇怪的情况让我很好奇,有人知道发生了什么吗?

float的精度通常约为 24 位,或大约 7 位小数。

您正在减去两个数量级相似的数字(第一个是r0+PI2,第二个是a1-PI2),因此正在经历显著性损失 - 结果的几个最高有效位为零,因此剩余的位更少来表示差异。这就是为什么答案只匹配大约 6 位小数的原因。

如果需要更高的精度,则double或 32 位或更大的定点表示可能比 float 更合适。还有一些任意精度的库可用,例如 GMP,它可以以您需要的所有精度表示数字,尽管算术会比内置类型慢得多。

你应该

使用fabs()函数而不是abs() abs()因为它只适用于整数。将abs()与浮点一起使用时,您会得到奇怪和错误的结果。

浮点数的行为不像数学实数。每 2 和可能导致"错误"。所以我不会仅仅因为一个例子就称第一个正确和第二个不正确。如果你想保持较小的误差,你需要小心你对浮点数所做的每一个动作。

如果数字的 abs 在同一范围内,则误差通常较小。如果范围不同,则误差往往更大。

例如,10000000.0 + 0.1 - 10000000.0几乎从不0.1

如果您知道输入的范围,则可以调整代码以减少错误。

相关文章: