在类似 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));
}
编辑:我是个傻瓜,宏参数也应该用括号括起来。感谢您的快速回复!抱歉常见错误 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));
}