在类似 C 函数的宏中实现浮点相等性检查:错误在哪里?

Implementing a float equality check in a C function-like macro: where's the bug?

编辑:我是个傻瓜,宏参数也应该用括号括起来。感谢您的快速回复!抱歉常见错误 post.

我有以下 main.c 文件

#include <stdio.h>

#define EPSILON (0.00000005f)

#define IsEqual(x,y) (    ( (x-y)*(x-y)  <= EPSILON  ) ? 1 : 0   )
//int IsEqual(float x, float y){ return  ( (x-y)*(x-y)  <= EPSILON  ) ? 1 : 0  ; }


int main(void)
{
    printf("\n10 == 3+3+4: %d\n", IsEqual(10, 3+3+4));
    printf("\n10 == (1/3.0f)*27 + 1: %d\n", IsEqual(10, (1/3.0f)*27 + 1));
    printf("\n10 == -1: %d\n", IsEqual(10, -1.0f));
    printf("\n10 == -1: %d\n", IsEqual(10, -1));
    printf("\n10 == 10: %d\n", IsEqual(10, 10));

    return 0;
}

这个输出是:

10 == 3+3+4: 0
10 == (1/3.0f)*27 + 1: 0
10 == -1: 0
10 == -1: 0
10 == 10: 1

这显然不是本意。但是,如果我注释掉类似函数的宏 IsEqual(x,y) 而不是取消注释函数 int IsEqual(float x, float y),那么我会得到预期的输出:

10 == 3+3+4: 1
10 == (1/3.0f)*27 + 1: 1
10 == -1: 0
10 == -1: 0
10 == 10: 1

我的宏定义有什么问题?编译器应该将 int * float 隐式转换为 float 或将 int - float 隐式转换为 float,因此这不应该是错误。我们还可以看到 EPSILON 不是太小而不能表示为小的正数 float32 因为当我使用函数而不是类似函数的宏时代码可以正常工作。

有什么建议吗?我希望今天能学到一些关于宏 and/or 浮点数的知识!

最佳 ~格雷格

改变这个

#define IsEqual(x,y) (    ( x*x - y*y  <= EPSILON  ) ? 1 : 0   )

#define IsEqual(x,y) (    ( (x)*(x) - (y)*(y)  <= EPSILON  ) ? 1 : 0   )

因为宏在预处理时展开

First will expand to:

printf("\n10 == 3+3+4: %d\n", ( ( 10*10 - 3+3+4*3+3+4 <= (0.00000005f) ) ? 1 : 0 ));
printf("\n10 == (1/3.0f)*27 + 1: %d\n", ( ( 10*10 - (1/3.0f)*27 + 1*(1/3.0f)*27 + 1 <= (0.00000005f) ) ? 1 : 0 ));
printf("\n10 == -1: %d\n", ( ( 10*10 - -1.0f*-1.0f <= (0.00000005f) ) ? 1 : 0 ));
printf("\n10 == 10: %d\n", ( ( 10*10 - 10*10 <= (0.00000005f) ) ? 1 : 0 ));

您使用 gcc 的 -E 进行检查将生成预处理的源代码。

Second will expand to:

printf("\n10 == 3+3+4: %d\n", ( ( (10)*(10) - (3+3+4)*(3+3+4) <= (0.00000005f) ) ? 1 : 0 ));
printf("\n10 == (1/3.0f)*27 + 1: %d\n", ( ( (10)*(10) - ((1/3.0f)*27 + 1)*((1/3.0f)*27 + 1) <= (0.00000005f) ) ? 1 : 0 ));
printf("\n10 == -1: %d\n", ( ( (10)*(10) - (-1.0f)*(-1.0f) <= (0.00000005f) ) ? 1 : 0 ));
printf("\n10 == -1: %d\n", ( ( (10)*(10) - (-1)*(-1) <= (0.00000005f) ) ? 1 : 0 ));
printf("\n10 == 10: %d\n", ( ( (10)*(10) - (10)*(10) <= (0.00000005f) ) ? 1 : 0 ));

谢谢。

您需要在参数周围加上括号:

(x)*(x) - (y)*(y)

这是您当前代码在预处理器之后的样子:

printf("\n10 == 3+3+4: %d\n", ( ( 10*10 - 3+3+4*3+3+4 <= (0.00000005f) ) ? 1 : 0 ));

IMO 应该这样实现,因为如果乘法你可以降低精度,不相等的数字将变得相等。

在你的算法中你还需要检查参数的符号

#define EPSILON (0.00000005f)
#define isEqual(x,y)     (fabs((x) - (y)) <= EPSILON)

int main() 
{
    printf("\n10 == 3+3+4: %d\n", isEqual(10, 3+3+4));
    printf("\n10 == (1/3.0f)*27 + 1: %d\n", isEqual(10, (1/3.0f)*27 + 1));
    printf("\n10 == -1: %d\n", isEqual(10.0, -1.0f));
    printf("\n10 == -1: %d\n", isEqual(10.0, -1));
    printf("\n10 == 10: %d\n", isEqual(10, 10));
}