11

我正在使用 Visual Studio 6 和一些用 c 编写的旧代码。我发现代码看起来像这样的问题..

int x = 3;
float y = 3.0;

if(x == y){
   do some crazy stuff
}

这是一个有效的比较吗?是否有可能在运行时分配浮点数为 3.0000001,这会失败?

4

11 回答 11

18

这通常(即,总是)是个坏主意。正如您所怀疑的,从 3 到 3.0000001 的比较确实会失败。

如果确实需要进行 int-float 比较,大多数人所做的就是选择一些容差阈值并继续使用,如下所示:

int x = 3;
float y = 3.0;

// some code here

float difference = (float) x - y;
float tolerableDifference = 0.001;

if ((-tolerableDifference <= difference) && (difference <= tolerableDifference)) {
    // more code
}
于 2009-07-21T19:23:57.893 回答
7

我将在这里逆势而上。对于第一个比较是否有效的问题,答案是肯定的。这是完全有效的。如果您想知道浮点值是否正好等于 3,那么与整数进行比较就可以了。整数被隐式转换为浮点值以进行比较。事实上,以下代码(至少在我使用的编译器中)产生了相同的汇编指令。

if ( 3 == f )
    printf( "equal\n" );

if ( 3.0 == f )
    printf( "equal\n" );

所以这取决于逻辑和预期目标是什么。语法本身并没有错。

于 2009-07-21T19:44:52.857 回答
5

还没有其他人引用它,我也有一段时间没有链接到它,所以这里是关于浮点表示和算术的可怕边缘的经典论文:每个计算机科学家应该知道的关于浮点的知识。

对于非数学家来说,这篇论文是一本具有挑战性的阅读文章,但在支持它们的大量数学之间,关键点得到了很好的阐述。

对于本次讨论,此处其他答案提出的观点都是有效的。浮点运算是不精确的,因此精确相等的比较通常是一个坏主意。因此,epsilon 是您的朋友。

精确比较规则的一个例外是对精确零的测试。在除法或对数之前准确地测试零是完全合法的并且通常是明智的,因为对于任何非零值的答案都是明确定义的。当然,在存在 IEEE 规则和 NaN 的情况下,您可以让它滑动并稍后测试 NaN 或 Inf。

于 2009-07-21T21:02:36.507 回答
3

好吧,我想你不会太惊讶听到比较浮点数是否相等是一个新手错误。

问题是许多小于整数值的增量实际上不能用 IEEE 浮点数精确表示。因此,如果您通过尝试将浮点数“索引”到 3.0 的值(例如以 0.1 为增量)来达到浮点数,那么您的相等比较很可能永远不会为真。

仅从类型强度的角度来看,这也是一个坏主意。您应该将浮点数转换为 int,检查您的 int 是否“足够接近”(例如 < 3.1 和 > 2.9 或类似的值),或者如果您试图让该浮点数为计数器之类的东西做双重任务,那就更好了,避免整个想法。

于 2009-07-21T19:58:29.820 回答
2

这很可怕。(我想知道你还会发现什么。)

x 将被提升为浮动,但这对您没有帮助。由于浮点数的表示方式,使用 == 比较它们是不可靠的。

我可能会建议这样的事情(检查绝对错误/差异):

#define EPSILON 0.0001 
if (fabs((float)x - y) < EPSILON) { /* Do stuff. */ }

如果您的 x 和 y 值“不错”,这是一种常用方法,可能足以满足您的目的。如果您真的想深入了解比较浮点数的主题,这篇文章可能比您想要的信息更多。它确实提到了 epsilon 方法:

如果预期结果的范围是已知的,那么检查绝对误差是简单而有效的。只需确保您的绝对误差值大于您正在处理的浮点范围和类型的最小可表示差异。

于 2009-07-21T19:41:51.303 回答
2

不,在您的用例中没有问题,因为整数被精确映射到浮点数(没有十进制截断问题,例如 0.3;但 3 是1.1E10二进制科学记数法)。

在我能想到的最坏情况下,可能会有整数无法用浮点数表示,因为两个连续浮点数之间存在大于 1 的“间隙”,但即使在这种情况下,当整数被转换为浮点数时进行比较,它将被截断为最近的浮点数,与浮点字面量相同。

因此,只要您的浮点数来自非十进制文字,与等效整数的比较将是相同的,因为在进行比较之前整数将被转换为相同的浮点数。

于 2009-07-21T19:44:46.790 回答
2

对于您的具体示例,将执行“做一些疯狂的事情”。3.0 在运行时不会是 3.0000001。

其他答案更适用于一般情况,但即使是硬编码的 epsilon 也不是世界上最伟大的想法。基于所涉及的实际数字的动态 epsilon 要好得多,因为数字越正越负,硬编码的 epsilon 相关的可能性就越小。

于 2009-07-21T19:58:55.293 回答
1

问题的症结在于,在基数为 10 的十进制中具有有限表示的浮点数并不总是在基数为 2 的二进制中具有有限表示。

于 2009-07-21T19:25:11.790 回答
1

如果代码看起来像您发布的内容(没有干预计算),那么就会出现一个问题,即3.0(float)3(因为整数自动转换为浮点数)是否相同。我认为在这种情况下它们保证是相同的,因为 3 完全可以表示为float.

旁白:即使整数不能完全表示为浮点数(即,如果它真的很大),我会想象在大多数实现中,x.0并且(float)x会是相同的,因为x.0如果不是,编译器首先将如何生成做某事就像(float)x?但是,我想这不是标准所保证的。

于 2009-07-21T19:38:35.587 回答
1

您可能对游戏开发者大会的讲座Numerical Robustness for Geometric Calculations 感兴趣(又名 EPSILON 不是 0.00001!)。它详细说明了为各种任务选择好的阈值/ε 值。

(在另一个答案中也提到了“每个计算机科学家应该知道的关于浮点的知识”的 +1。)

于 2009-07-21T21:15:01.973 回答
-1

编辑

正确的方法是使用epsilon方法:

#include <math.h>
int x = 3;
int y = 3.0;
if (fabs((float) x - y) < 0.0001) { // Adjust the epsilon
  // Do stuff
}
于 2009-07-21T19:25:21.463 回答