Chinaunix

标题: float 能作为 bool 使用? [打印本页]

作者: linjxwell    时间: 2015-09-24 11:13
标题: float 能作为 bool 使用?
公司代码里看到有用 float 作为 if 语句的判断, 所以写了以下的测试程序, 结果在 vs2010 和 g++ 4.8.4 都会输出 false 那一行. 我疑问: 0.0f 的位模式和整形 0 是一样的? 还是说编译器处理了?
  1. #include <cstdio>

  2. int main(int argc, char* argv[])
  3. {
  4.         float f = 0.0f;
  5.         if (f)
  6.                 printf("0.0f true\n");
  7.         else
  8.                 printf("0.0f false\n");

  9.         printf("===\n");

  10.         union GameValue
  11.         {
  12.                 int nValue;
  13.                 float fValue;
  14.         };

  15.         GameValue gv;
  16.         gv.fValue = 0.0f;
  17.         if (gv.nValue)
  18.                 printf("gv.fValue==0.0f true\n");
  19.         else
  20.                 printf("gv.fValue==0.0f false\n");

  21.         return 0;
  22. }
复制代码

作者: hellioncu    时间: 2015-09-24 11:22
部分浮点数能精确表示。
反正别这么用。
作者: bruceteen    时间: 2015-09-24 12:12
0.0f 的位模式和整形 0 是一样的?
------ 确实可能一样,但和你的问题无关,if (f) 相当于 if( f !=0 )

给你一个示例:
  1. #include <stdio.h>
  2. #include <string.h>

  3. int main( void )
  4. {
  5.     unsigned a = 0x80000000; // 看好,这里不是0
  6.     float b;
  7.     memcpy( &b, &a, sizeof(b) );

  8.     if( b )
  9.         printf( "true\n" );
  10.     else
  11.         printf( "false\n" ); // 执行这一句

  12.     return 0;
  13. }
复制代码
b的位模式和整型0是不一样的,但仍然执行else那句。

C标注如是说:
if( expression ) statement
The substatement is executed if the expression compares unequal to 0.

作者: safedead    时间: 2015-09-24 16:26
回复 3# bruceteen


你这段代码被gcc4.4.7编译成了如下汇编代码,条件判断被直接干掉了
gcc -Wall -O2 test.c -S
  1.         .file   "test.c"
  2.         .section        .rodata.str1.1,"aMS",@progbits,1
  3. .LC0:
  4.         .string "false"
  5.         .text
  6.         .p2align 4,,15
  7. .globl main
  8.         .type   main, @function
  9. main:
  10. .LFB30:
  11.         .cfi_startproc
  12.         movl    $.LC0, %edi
  13.         subq    $8, %rsp
  14.         .cfi_def_cfa_offset 16
  15.         call    puts
  16.         xorl    %edi, %edi
  17.         call    exit
  18.         .cfi_endproc
  19. .LFE30:
  20.         .size   main, .-main
  21.         .ident  "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-16)"
  22.         .section        .note.GNU-stack,"",@progbits
复制代码

作者: safedead    时间: 2015-09-24 16:30
回复 2# hellioncu


   
同意,强烈建议不要这么用,编译器如何私下处理这种东西是难以测度的
作者: bruceteen    时间: 2015-09-24 16:35
回复 4# safedead
这是两回事
之所以会优化掉,是因为编译器知道if(f)必然不成立;之所以知道if(f)必然不成立,是因为f在编译期间就确定了0值。

或者这么说:
假如你用 scanf 输入 a 值 0x80000001,那么编译器就没法优化,但依然输出 false
   
作者: safedead    时间: 2015-09-24 16:54
回复 6# bruceteen

我把代码稍微改了下
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdint.h>
  4. #include <stdlib.h>
  5. #include <unistd.h>

  6. int foo(float b)
  7. {
  8.         if( b )
  9.                 printf( "true\n" );
  10.         else
  11.                 printf( "false\n" );

  12.         return 0;
  13. }

  14. int main(int argc, char *argv[])
  15. {
  16.         foo(0.0f);
  17.         foo(0.1f);
  18.         exit(EXIT_SUCCESS);
  19. }
复制代码
编译执行:

[root@sxy-lenovo ~]# gcc -Wall -O2 test.c
[root@sxy-lenovo ~]# ./a.out
false
true

很有趣是不?(虽然是恶趣味)

查看汇编代码,if那句被翻译成下面这段:
  1.         ucomiss .LC0(%rip), %xmm0
  2.         jp      .L9
  3.         jne     .L9
复制代码
看到这段我就震惊了,jne的含义好说,不等于,jp是啥意思?为奇数!

楼主贴的代码看来没大家想的那么简单,如果是产品代码,里面估计还有其它含义

作者: linjxwell    时间: 2015-09-24 18:17
本帖最后由 linjxwell 于 2015-09-24 18:22 编辑

回复 7# safedead

应该没什么神秘的含义, 主要是那个 union 的代码, 原来是
  1. if (gv.fValue)
复制代码
后来被人改掉了, 改为
  1. if (gv.nValue)
复制代码
并写上注释说(大意): 用 float 作 bool 是不对的

然后我就不知道为什么不行?
如果是我改的话, 我会改为
  1. if (gv.fValue > 0.0f)
复制代码
因为代码后面是 gv.fValue 的值去乘其他的数, 所以, 这里 if 语句主要是判断不等于 0 的情况, 但是又因为用到了 union, 所以, 感觉改的不对, 而又有疑惑~
而我又没学过汇编..
作者: safedead    时间: 2015-09-24 19:50
回复 8# linjxwell


汇编不能用于理解C语言语法,仅仅能作为一个实现的参考
float不是简单的数据,它是有特定结构的,有兴趣的话可以去看IEEE754浮点数的资料
正因为它具有特定结构,所以不能简单的比较

在if语句中,使用float通常要用标准的浮点表达式,常见的规则之一就是不推荐用 "=="去和浮点常数比较
一个简单的10进制浮点数转化为2进制浮点数很可能变成无限循环小数从而产生误差

   
作者: bruceteen    时间: 2015-09-25 10:19
回复 8# linjxwell
作者搞错了吧
根据C标准,只要是scalar type就可以作为if的表达式,所以 if( float ) 是完完全全符合标准的,而且C标准明确说了它相当于 if( float != 0 )
反而,作者通过union存进去float,取出来int,这种行为却是未定义行为,可以google一下 类型双关(type punning) 和 严格别名(strict aliasing) 相关的内容
作者: linjxwell    时间: 2015-09-25 12:09
回复 10# bruceteen

谢谢你的资料提示!
但我想我没有搞错, 我们的程序中不允许出现负值的, 所以, 我改为了
  1. if (fValue > 0.0f)
复制代码

作者: shang2010    时间: 2015-09-25 13:49
bool是计算机最基本的数据类型,是用来表示二进制的,
什么数字字母人工智能都是bool慢慢堆起来的,float是什么??
作者: hevake_lcj    时间: 2015-09-26 18:24
不管编译器是否支持,我们都不能这么干。
规规矩矩按语法编法,不要去挑战编译器的下限,说不定哪个编译不支持了呢?吃亏的还不是你自己。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2