为什么我的比较运算符不比较我的点长度和双精度值?

Why my compare operator is not comparing my point length and double value?

我创建了新的运算符来比较我的点向量长度和双精度值。我有条件容忍小于 0.01 的差异。 当我使用此运算符比较我的 Point 和我的双精度值时(接近后的两个值相同)但运算符 == 未返回 true。

class Point {
private:
    double x, y, z;
public:
    Point() { x = y = z = 0.0; }
    Point(double v) { x = y = z = v; }
    Point(double x, double y, double z){
        this->x = x; this->y = y; this->z = z;
    }
    double getLength(){
        return sqrt(pow(x,2)+pow(y,2)+pow(z,2));
    }
    friend const bool operator== (double &d, Point &v);
};

double approach(double num){
    return floor(num*100)/100;
}

const bool operator== (const double &d, Point &v){
    return (approach(d) == approach(v.getLength()));
}

int main()
{
    Point p1(3,4,1);
    cout << p1.getLength() << endl; // 5.09902
    cout << approach(p1.getLength()) << endl;
    cout << approach(5.091) << endl;
    if(5.091 == p1)
        cout << "True";
    return 0;
}

这个问题可以在 32 位 Intel 架构的 gcc 上重现,无需优化。 这是它发生的一个例子: compiler explorer example.

这是由于臭名昭著的 323 bug gcc,它很难与 Intel 的 80 位浮点寄存器一起工作,它比类型 double 的 64 位更宽。一些值在 80 位寄存器中结束,一些值在 64 位内存值中。

在您的情况下,首先调用 approach(d),然后在调用 v.getLength() 时溢出到内存中。另一方面,approach(v.getLength()) 的值不会被溢出并获得寄存器的所有 80 位精度。

比较 80 位精确值和截断的 64 位值时,比较结果为 false

一个可能的解决方案是避免在 approach() 中除以 100,因为它会引入额外的位。相反,您可以尝试:

static constexpr double magnitude = 100.0;
const bool operator== (double d, const Point &v){
    return floor(d * magnitude) == floor(v.getLength() * magnitude));
}