浮点数比较为啥没有相等的函数
Posted
技术标签:
【中文标题】浮点数比较为啥没有相等的函数【英文标题】:floating point number comparison why no equal functions浮点数比较为什么没有相等的函数 【发布时间】:2019-12-10 08:29:28 【问题描述】:在浮点数的c++标准中,有std::isgreater
比较大,std::isless
比较少,那为什么没有std::isequal
比较相等呢?是否有一种安全准确的方法来检查double
变量是否等于标准定义的DBL_MAX
常量?我们尝试这样做的原因是我们通过服务协议访问数据,它定义了一个双字段,当没有数据可用时它将发送DBL_MAX
,所以在我们的客户端代码中,当它是DBL_MAX
时,我们需要跳过它,以及我们需要处理的任何其他内容。
【问题讨论】:
因为比较浮点数的相等性并不好,因为它们容易出现舍入错误。我认为this question 会帮助你。 请查看问题右侧的相关问题列表,其中许多会解释为什么浮点相等比较通常是不可能的。 比较本质上不准确的数字是否相等有什么实际用途?特别是DBL_MAX
,这是一个荒谬的巨大数字,精度低得荒谬?如果您告诉我们您到底想要做什么,也许我们可以找到更好的解决方案来实现这一目标。
为什么不a==b
?
@fluter 如果你“在某处读到” "operator ==
不适用于 float
/double
" 那么你需要做更多的阅读,因为这不是真的。事实上,这完全是危险的。假设的std::isequal
并不能解决问题。请参阅docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html。
【参考方案1】:
isgreater
、isless
、isgreaterequal
、islessequal
与 >
、<
、>=
和 <=
相比的兴趣在于它们不会引发 FE_INVALID(浮点异常,这些是与 C++ 异常不同的野兽,并且不会映射到 C++ 异常)与 NaN 进行比较时,运算符会这样做。
由于==
不会引发 FP 异常,因此不需要额外的功能。
注意还有islessgreater
和isunordered
。
如果您不考虑 NaN 或不测试浮点异常,则无需担心这些函数。
考虑相等比较==
是如果您想检查值是否相同(忽略与有符号 0 和 NaN 相关的问题)。根据您达到这些值的方式,有时考虑近似相等比较很有用 - 但不建议系统地使用一个,例如这种近似相等可能不具有传递性。
在您的网络协议上下文中,您必须考虑数据是如何序列化的。如果序列化被定义为二进制,您可能可以重建确切的值,因此 ==
是您想要的,因此请与 DBL_MAX
进行比较(对于其他值,请检查为有符号 0 和 NaN 指定的内容,并知道有信号和安静的 NaN 由不同的位模式表示,尽管 IEEE 754-2008 现在推荐其中之一)。如果表示为十进制,则必须检查表示是否足够精确,以便 DBL_MAX
值可重构(并注意舍入模式)。
请注意,我会考虑使用 NaN 来表示没有可用数据的情况,而不是使用可能有效的值。
【讨论】:
【参考方案2】:是否有安全准确的方法来检查双变量是否等于标准定义的
DBL_MAX
常量?
当您通过评估某个表达式获得浮点数时,由于有限精度和舍入误差,==
比较在大多数情况下没有意义。但是,如果您首先将浮点变量设置为某个值,那么您可以使用 ==
将其与该值进行比较(有一些例外,例如正零和负零)。
例如:
double v = std::numeric_limits<double>::max();
conditional assignment to v of a non-double-max value
if (v != std::numeric_limits<double>::max())
process(v);
std::numeric_limits<double>::max()
完全可以表示为 double
(它是 double
),因此这种比较应该是安全的,并且永远不会产生 true
,除非重新分配了 v
。
【讨论】:
我很好奇为什么 会有意义,因为两个操作数上可能会累积错误导致错误结果,就像 == 一样。即使是额外的精度(如 80 位寄存器)也可能导致在琐碎的情况下出现灾难性的 也会被破坏并且毫无意义...... @aka.nicestd::isless
/ std::isgreater
服务于不同的目的:他们suppress floating-point exceptions。所有比较的精度问题仍然存在。从这个意义上说,它们都是“破碎的”。
所以你同意正确的答案是不引发异常,而不是因为不准确而没有意义,就像 AProgrammer 解释的那样。
@aka.nice,我同意。但是OP问了两个问题。第一个是关于std::isgreater
和std::isless
。第二个是关于如何检测DBL_MAX
。 程序员回答了第一个,我回答了第二个。
好的,我现在更明白你的回答了。但是我们不知道使用的协议,如果是通过的ASCII十进制值,则必须对其进行解码,并且这些计算也需要一些保证...【参考方案3】:
浮点数之间的相等函数没有意义,因为浮点数有四舍五入。
因此,如果您想比较 2 个浮点数是否相等,最好的方法是声明一个显着的 epsilon(error) 进行比较,然后验证第一个数字是否在第二个左右。
为此,您可以验证第一个数字是否大于第二个数字减去 epsilon 并且第一个数字是否小于第二个数字加上 epsilon。 例如。
if ( first > second - epsilon && first < second + epsilon )
//The numbers are equal
else
//The numbers are not equal
【讨论】:
测试first = second = DBL_MAX
的比较。
是的,他可以比较这两个数字是否等于 DBL_MAX,但这只有在初始化后没有进行任何操作时才有可能。但是正确的表达是first == second == DBL_MAX
虽然近似相等测试有时很有用,但请不要推荐它们作为相等的默认替代品,因为它们缺少重要的相等属性,例如传递性。
是的,当您应用此比较时,传递性属性丢失的逻辑是,但无论如何,传递性属性在数字的舍入过程中已经丢失......只是精度问题。因此传递性属性保持在小于精度或到某个小数位。
Evg 指出,当first = second = DBL_MAX
时,此函数将返回false,即使数字在任何意义上都是相等的。任何大于您选择的 1/epsilon
的 double 都会发生同样的情况 - 您永远不会得到 second - epsilon == second == first
和 second + epsilon == second == first
的相等性,因此 >
和 <
都会产生 false
。以上是关于浮点数比较为啥没有相等的函数的主要内容,如果未能解决你的问题,请参考以下文章