C ++浮点陷阱[重复]

Posted

技术标签:

【中文标题】C ++浮点陷阱[重复]【英文标题】:C++ Floating point gotcha [duplicate] 【发布时间】:2011-10-15 23:04:04 【问题描述】:

可能重复:Most effective way for float and double comparison

我是 C++ 新手。在阅读 C++ 时,我有一个疑问。 如何判断两个浮点数是否相等?

提前致谢

【问题讨论】:

可以帮助你java2s.com/Tutorial/Cpp/0040__Data-Types/… @jholar99:答案基本上是:“你决定两个浮点数如何相互相等。” 如果在您的编程生涯中的任何时候,您都在比较两个浮点是否完全相等,那么 99.9999999999 % 的时间您是真的、真的错了。 @Stefano 不,绝对不是 99.9999999%,也许是 80%,因为有时精确的二进制相等就足够了,甚至是必需的。 @Martinho +1 获得唯一完全正确的答案,尽管对初学者帮助不大。 【参考方案1】:

您需要了解一个特殊的常量,称为 DBL_EPSILON(或 FLT_EPSILON)。这是可以添加到 1.0 并更改其值的最小值。 1.0 的值非常重要——添加到 DBL_EPSILON 时较大的数字不会改变。现在,您可以将此值缩放到您要比较的数字,以判断它们是否不同。比较两个双精度的正确表达式是:

if (fabs(a-b) <= DBL_EPSILON * fmax(fabs(a), fabs(b)))

    // ...

【讨论】:

FLT_EPSILON不是可以添加到 1.0f 并更改其值的最小值。 blog.frama-c.com/index.php?post/2013/05/09/FLT_EPSILON @PascalCuoq,感谢您的指点。【参考方案2】:

如果您的浮点类型使用 IEEE 754 表示(很可能是这种情况),那么您应该使用浮点的 二进制表示 的顺序与 按价值排序。也就是说,如果将浮点数的二进制表示增加一位,则会得到下一个更大的数字。

利用这个事实,我们可以通过计算它们的二进制差异来比较浮点数。这称为“按最后一个单元 (ULP) 进行比较”。有一些涉及符号、零、无穷大和 NaN 的微妙之处,但这就是它的要点。这是comprehensive article 解释这一点。

基本上,我们认为两个浮点数相等,如果它们在最后一个位置的少数几个单位不同。结合编译器关于其数学函数精度的文档和您自己的代码,您可以确定哪个截止值适合您的需求。

在伪代码中:

double x, y;

// this is type punning, should be done differently in reality
uint64_t ux = *reinterpret_cast<const uint64_t*>(&x);
uint64_t uy = *reinterpret_cast<const uint64_t*>(&y);

return abs(ux - uy) < CUT_OFF; // e.g. CUT_OFF = 3;

上面的代码只是一个粗略的例子,它不会工作,在最后的比较之前你必须处理很多特殊情况。有关详细信息,请参阅文章。

【讨论】:

我喜欢这个想法,但是如果 ux @Spraff:这只是粗略的要点。实际上,您必须更仔细地实施它(例如,首先转换为有符号整数,或者先比较然后以正确的顺序减去)。 我想你可以要求所有非尾数部分相等,然后将它们屏蔽掉...... 好吧,任何明智的实现都将从if (sign(a) != sign(b)) return a == b开始,所以我们以后可能不必担心这个。 我的意思是如果指数不同,那么值就有一个数量级的差异,这可能已经足够不同了:-P【参考方案3】:

显然,您不应该使用operator == 来比较它们。

这里的重要概念是,如果您的两个浮点数之差足够小到您要解决的问题的精度要求或小于您的误差范围,我们应该将它们视为相等。

有一些实用的方法建议如

  fabs(f1 - f2) < precision-requirement
  fabs(f1 - f2) < max(fabs(f1), fabs(f2)) * percentage-precision-requirement

【讨论】:

啊,这取决于。有时精确的二元相等就足够了,甚至是必需的。无论如何,许多“神奇”常量(如 0、1、整数)都是精确的,并且您不希望几乎相等的值被视为相等。这些可能被视为特殊情况,但并不罕见。所以我不会总是称== 是错误的解决方案,而是根据情况权衡它。当然,这需要更多地了解不精确的浮点表示。 差异(第一个版本)的问题在于它没有给你在所有尺度上的“接近度”相同的度量。接近零的数字需要比接近范围末端的数字更精细的precision_requirement 这不是一个正确的答案。首先,精度要求可能小于 epsilon。其次,您必须按 f1 和 f2 的最大值进行缩放,而不是最小值。

以上是关于C ++浮点陷阱[重复]的主要内容,如果未能解决你的问题,请参考以下文章

C中浮点数组的Memset问题[重复]

Bash中具有浮点值的C风格算术[重复]

在 C 中使用 sizeof 运算符分配浮点数据类型(5.0)而不是 4 个字节 [重复]

与顺序无关的浮点求和[重复]

c ++中较大值的数学计算[重复]

我如何从c ++中的函数返回多个值[重复]