分享
三行代码  ›  专栏  ›  技术社区  ›  Gregory Fenn

在像宏这样的C函数中实现浮点等式检查:错误在哪里?

  •  1
  • Gregory Fenn  · 技术社区  · 1 周前

    编辑:我是个傻瓜,宏参数也应该用括号括起来。谢谢你的快速回复!对不起,常见的错误帖子。

    我有以下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 ,所以这不应该是bug。我们也可以看到 EPSILON 不是太小,不能代表一个小的正数 float32

    有什么建议吗?我希望今天能学到一些关于宏和/或浮动的知识!

    最好的 ~格雷格

    2 回复  |  直到 1 周前
        1
  •  2
  •   klutt    1 周前

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

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

    printf("\n10 == 3+3+4: %d\n", ( ( 10*10 - 3+3+4*3+3+4 <= (0.00000005f) ) ? 1 : 0 ));
    
        2
  •  0
  •   Sri lakshmi kanthan    1 周前

    改变这个

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

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

    因为宏在预处理时被扩展

    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将生成预处理的源代码。

    第二个将扩展到:

    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 ));
    

    谢谢。

        3
  •  0
  •   P__J__    1 周前

    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));
    }