std::更大<double>和 std::使用起来不太<double>安全吗?

Is std::greater<double> and std::less<double> safe to use?

本文关键字:std gt double lt 安全 更大 起来      更新时间:2023-10-16

当使用<,>,=,!=运算符比较C++中的double值时,我们不能总是确定结果的正确性。这就是为什么我们使用其他技术来比较doubles,例如,我们可以通过测试它们的差异是否真的接近于零来比较两个替身a和b。我的问题是,C++标准库是使用这些技术实现std::less<double>std::greater<double>,还是仅仅使用了不安全的比较运算符?

您可以100%确信这些运算符的结果的正确性。只是之前的计算可能会导致截断,因为二重的精度不是无限的。所以运算符是非常好的,只是你的操作数不是你期望的

因此,用什么进行比较并不重要。

它们使用标准运算符。以下是stl_function.h头文件中std::greater的定义

  templatete<typename _Tp>
    struct greater : public binary_function<_Tp, _Tp, bool>
    {
      bool
      operator()(const _Tp& __x, const _Tp& __y) const
      { return __x > __y; }
    };

operator<operator>至少尽可能给出正确的结果。然而,使用浮点算法,尤其是double算法,还涉及一些基本问题。这些并没有通过使用您提到的比较函数来减少,因为它们是当前CPU使用的浮点表示所固有的。

至于函数std::less/std::greater:它们只是标准运算符的打包版本,用于STL算法中需要二进制谓词时。

double值具有64位表示,而英特尔CPU最初的"双"运算是用80位完成的。一开始"免费"获得更高的精度听起来不错,但这也意味着结果取决于编译器是否允许代码直接使用FPU寄存器(80位)或写回内存的值(四舍五入到64位)的中间结果。这种优化完全取决于编译器,没有任何标准定义。

为了使事情变得更复杂,现代编译器还可以使用较新的矢量指令(MMX/SSE),这些指令同样只有64位。上述问题不会出现在这种情况下。然而,这取决于编译器是否将这些指令用于浮点运算。

当差值仅在尾数的最后几位时,几乎相等的值中的较小值/较大值的比较总是会受到影响——它们总是会出现截断错误,并且您应该确保您的程序不会严重依赖于非常接近的值的比较结果。例如,当它们的差值小于阈值时,您可以认为它们相等,例如if (fabs(a - b)/a < factor*DBL_EPSILON) { /* EQUAL */ }DBL_EPSILON是在float.h中定义的,而factor取决于之前进行了多少可能的截断/舍入的数学运算,应该进行彻底的测试。我认为factor=16..32左右的值是安全的,但您的里程数可能会有所不同。

从cpprreference中显示

使用运算符<T型

这意味着,除非您在double上特别重载了operator<operator>以实现正确的比较,否则使用std::lessstd::greater将不会存在正确的比较。

IOW您可以使用std::greaterstd::less,但它将使用标准比较,除非您专门实现一些operator<operator>,以使用它们的差异小于std::numeric_limits<double>::epsilon() 来正确比较doublefloat