std::更大<double>和 std::使用起来不太<double>安全吗?
Is std::greater<double> and std::less<double> safe to use?
当使用<,>,=,!=
运算符比较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::less
或std::greater
将不会存在正确的比较。
IOW您可以使用std::greater
或std::less
,但它将使用标准比较,除非您专门实现一些operator<
或operator>
,以使用它们的差异小于std::numeric_limits<double>::epsilon()
来正确比较double
或float
- 使用std::multimap迭代器创建std::list
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 从持续时间构造std::chrono::system_clock::time_point
- std::具有相同基类的类的变体
- std::向量与传递值的动态数组
- 使用std::vector的OpenCL矩阵乘法
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- std::condition_variable::wait()如何评估给定的谓词
- 如何获取std::result_of函数的返回类型
- std::原子加载和存储都需要吗
- 将对象移动到std::shared_ptr
- std::vector<;uint8_t>;当C++11/14启用时,手动复制而不是调用memcpy
- 是std :: set&lt; std :: future&gt;不可能存在
- 在修改列表后,std :: list&lt; t&gt; :: end()的值是否会更改
- 使用 std::vector<boost::shared_ptr<Base_Class>> 或 boost::p tr_vector 的性能注意事项是什么<Base>
- std::map<std::set, double> AND std:<long>map< std::p air<long, long>, double>
- 如何获取std::vector<DMatch>