double
在 C++ 中使用运算符比较值时<,>,=,!=
,我们不能始终确定结果的正确性。这就是我们使用其他技术进行比较的原因doubles
,例如,我们可以通过测试它们的差异是否真的接近于零来比较两个双精度数 a 和 b。我的问题是,C++ 标准库是实现std::less<double>
和std::greater<double>
使用这些技术,还是只使用不安全的比较运算符?
4 回答
您可以 100% 确定这些运算符的结果的正确性。只是先前的计算可能导致截断,因为双精度不是无穷无尽的。所以运算符非常好,只是你的操作数不是你期望的那样。
因此,您使用什么进行比较并不重要。
operator<
并operator>
至少尽可能给出正确的结果。但是,使用浮点算术存在一些基本问题,尤其是double
. 使用您提到的比较函数不会减少这些,因为它们是当前 CPU 使用的浮点表示所固有的。
至于函数std::less
/ std::greater
:它们只是标准运算符的打包版本,旨在在 STL 算法中需要二进制谓词时使用。
一个double
值具有 64 位表示,而 Intel 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
,但你的里程可能会有所不同。
他们使用标准运算符。这是 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; }
};
从cppreference它说
在类型 T 上使用 operator<。
这意味着除非有一个operator<
oroperator>
你已经特别重载double
来实现正确的比较,否则你使用std::less
or将不会有正确的比较std::greater
。
IOW 您可以使用std::greater
或std::less
但它将使用标准比较,除非您实施一些operator<
或operator>
专门使用它们的差异来正确比较double
s 或sfloat
std::numeric_limits<double>::epsilon()