如何为基本类型(浮点型)编写替换,以覆盖 运算符 == C++

How to write a drop-in replacements for fundamental type (float) that override operator== in C++?

本文关键字:替换 覆盖 C++ 运算符 类型 浮点型      更新时间:2023-10-16

我们不能直接比较二进制浮点数。我正在为浮点数编写一个插入式替换类覆盖 bulti-in 比较运算符:

template<class U>
class Float {
    private:
        U val;
    public:
        Float(U v = 0): val(v) {}
        operator U() const { return val; }
        friend bool operator<(Float a, Float b) { return a.val + 1e-6 < b.val; }
        friend bool operator==(Float a, Float b) { return !(a < b) && !(b < a); }
        friend Float operator*(Float a, Float b) { return a.val * b.val; }
        template<class T>
        friend Float operator*(T a, Float b) { return a * b.val; }
        template<class T>
        friend Float operator*(Float a, T b) { return a.val * b; }
};

现在我们可以写这样的东西:

#include<assert.h>
int main() {
    Float<double> a = 0.2, b = 0.02;
    assert(a * a == 2 * b);
}

但是,此代码将显示意外行为:

#include<complex>
int main() {
    std::complex< Float<double> > a(0.2);
    a * 2.0;
}

它会一次又一次地呼唤Float::operator*(std::complex<Float<double> >, Float)就像一个递归的无限循环,最后得到堆栈溢出。如何解决这个问题?

编辑

DeadMG和Charles Bailey指出,来自ISO/IEC 14882:2011,26.4:"实例化除浮点数,双精度或长双精度以外的任何类型的模板复合体的效果未指定。

也许我举了一个错误的反例。我们仍然可以讨论如何为基本类型编写一个好的替换类。

让我澄清我的动机,assert(0.1 * 0.1 == 0.01);违反直觉。这就是为什么我写了一个 Float 类,它使用"几乎相等"的行为来比较两个浮点数。

使构造函数显式。

a * 2.0;

隐式构造一个浮点型,然后调用:

template<class T>
friend Float operator*(T a, Float b) 
{
    return a * b.val;
}

这反过来又在 b.val 上调用 * 运算符时隐式构造另一个 Float; 然后你从那里递归。

我提出完全修复之前,您需要进一步充实您的预期行为。

我用来探索它并测试修复的代码:

#include <iostream>
template<class U>
class Float {
    private:
        U val;
    public:
        explicit Float(U v = 0): val(v) 
        {
            std::cout << "constructor ";
        }
        operator U() const { return val; }
        friend bool operator<(Float a, Float b) { return a.val + 1e-6 < b.val; }
        friend bool operator==(Float a, Float b) { return !(a < b) && !(b < a); }
        friend Float operator*(Float a, Float b) { return a.val * b.val; }
    template<class T>
    friend Float operator*(T a, Float b) 
    {
        std::cout << "here";
        return a * b.val;
     }
        template<class T>
        friend Float operator*(Float a, T b) { return a.val * b; }
};
#include<complex>
int main() {
    std::complex< Float<double> > a(0.2);
    a * 2.0;
}