浮点不准确以奇怪的方式破坏了幂等性

Floating point inaccuracies breaks idempotence in weird ways

提问人:Powereleven 提问时间:7/11/2022 最后编辑:Powereleven 更新时间:7/13/2022 访问量:137

问:

我想知道在归一化矢量(特别是 3D 矢量)时,浮点误差会有多严重。归一化一次应该给出与归一化两次相同的值,但正如预期的那样,浮点不准确并不能保证我们这一点。因此,我决定测试一些其他东西:如果你无限期地归一化,它会总是“收敛”到一个归一化为自身的值吗?答案是否定的。例如,几秒钟的测试,我发现了一个在 2: 和 之间循环的值。有些在 3、5 步后“收敛”。这是我无聊的代码:-0.60445204839058564 -0.54547899327834748 -0.58059485795902943-0.60445204839058575 -0.54547899327834759 -0.58059485795902954

#include <iostream>
#include <numeric>
#include <random>

using namespace std;

struct Point
{
    double x, y, z;
    bool operator==(const Point& o) const
    {
        return
            x == o.x &&
            y == o.y &&
            z == o.z;
    }
};

auto square(auto x)
{
    return x * x;
}

Point normalize(const Point& p)
{
    auto l = sqrt(square(p.x) + square(p.y) + square(p.z));
    return { p.x / l, p.y / l, p.z / l };
}

std::mt19937 mt(24);
std::uniform_real_distribution d(-1., 1.);

int main()
{
    while (true)
    {
        auto
            x = d(mt),
            y = d(mt),
            z = d(mt);

        Point p{ x, y, z };

        auto
            n1 = normalize(p),
            n2 = normalize(normalize(p));
        int cnt = 1;
        while (n1 != n2)
        {
            n1 = n2;
            cnt++;
            n2 = normalize(n2);
            auto len = square(n2.x) + square(n2.y) + square(n2.z);
            if (len != 1.)
            {
                __debugbreak();
            }
        }
        if (cnt != 2)
        {
            cout << x << ' ' << y << ' ' << z
                << " took " << cnt << '\n';
        }
    }
}

所以我有一些好奇心:

  1. 哪些输入会产生最长的循环/周期,并且永远不会收敛?
  2. 哪些输入需要最长的时间才能收敛?
  3. 归一化一次后,什么输入给出长度为 1 的最大误差?
C++ 浮点 精度 IEEE-754

评论

0赞 user4581301 7/11/2022
一个问题中的三个问题。要小心。这可能使问题过于宽泛。如果你能更好地将这三者联系在一起,或者放弃一个问题,问题可能会吱吱作响。有时,一个问题可能会回答或暗示对其他问题之一的回答。
0赞 paddy 7/11/2022
我认为这很大程度上取决于向量分量被平方和相加时溢出的位。这就是精度损失发生的地方。而且平方根也不会是 100% 精确的。如果你的那最后几位精度是关键任务,那么你真的需要一个更宽/自定义的数据类型或更高级的规范化算法。
0赞 Powereleven 7/11/2022
我提出了许多问题来举例说明一个人如何想知道很多事情,但如果其中任何问题得到回答,我很高兴
1赞 Eric Postpischil 7/11/2022
我的错误是,即使真正的大小不为零,也有一些微小的坐标会生成零,然后归一化返回一些无限大小的向量,所以问题 3 的答案是一个微小的向量(例如 {2^−1000, 2^−1000, 2^-1000}),它会产生无穷大的误差。可以通过要求坐标位于其平方既不产生 0 也不产生 ±∞ 的范围内来排除这些情况,但这会排除一些可能有趣的情况,例如其中一个正方形为零但其他平方不是。square(p.x) + square(p.y) + square(p.z)
1赞 njuffa 7/12/2022
相关问题:stackoverflow.com/questions/53663382/...

答: 暂无答案