免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: sabrisu
打印 上一主题 下一主题

小问题:输出1.0/3.0*3结果等于1.000000? [复制链接]

论坛徽章:
4
天秤座
日期:2013-10-18 13:58:33金牛座
日期:2013-11-28 16:17:01辰龙
日期:2014-01-14 09:54:32戌狗
日期:2014-01-24 09:23:27
21 [报告]
发表于 2012-08-23 21:38 |只看该作者
0.3333......这东西,貌似用3进制表示容易一些。

论坛徽章:
1
辰龙
日期:2014-05-22 11:38:58
22 [报告]
发表于 2012-08-23 23:10 |只看该作者
  1. $ cat test.c
  2. int
  3. main(void)
  4. {
  5.         float   f = 1.0 / 3.0 * 3.0;
  6.         printf("%x\n", *((int *)&f));

  7.         return 0;
  8. }
  9. $ ./a.out
  10. 3f800000
复制代码
3f800000 = 0011 1111 1000 0000 0000 0000 0000 0000
正好是1.0的浮点数表示

不知对否

论坛徽章:
1
丑牛
日期:2013-09-29 19:04:50
23 [报告]
发表于 2012-08-23 23:20 |只看该作者
回复 19# x5miao


    常量你妹!


printf("%f",...)默认保存的是6位小数。
1.0/3.0=0.333333333333..
1.0/3.0*3.0=0.9999999999..
因为只能保存6位小数,所以就四舍五入成1.000000了。

如果是printf("%.9f",0.99999999),那结果就是0.999999999。

    常量你妹!

论坛徽章:
0
24 [报告]
发表于 2012-08-24 21:07 |只看该作者
sabrisu 发表于 2011-04-29 15:21
1.0/3.0结果应该是一个浮点数,是有效位数限定的,再乘3为什么还会是1呢?我看的是谭浩强老师的书,里边说“1 ...

如果希望结果不是1.00000000000000000的话,可以
  1. float f = 1.0 / 3.0;
  2. printf("%.10f\n", f*3.0);
复制代码
这样。

论坛徽章:
0
25 [报告]
发表于 2012-08-24 22:56 |只看该作者
回复 24# KanonInD


    非常遗憾,这样也是1.0。几乎所有浮点处理的路径(编译器优化、库函数输出)都有舍入的步骤,0.9,9循环绝对输出为1。

你想啊,0.999999.....的极限(n为小数位数)就是1。

论坛徽章:
0
26 [报告]
发表于 2012-08-25 00:30 |只看该作者
本帖最后由 x5miao 于 2012-08-25 00:44 编辑

回复 23# solu

static int i = 2 || 1 / 0;

给我解释一下这条语句是否合法,以及其值是多少?

    编译时常量表达式会被直接替换成常量。运行时根本就没计算1.0/3.0的这个过程,懂不?

四舍五入你妹啊。

论坛徽章:
0
27 [报告]
发表于 2012-08-25 07:54 |只看该作者
本帖最后由 KanonInD 于 2012-08-26 16:33 编辑
sonicling 发表于 2012-08-24 22:56
回复 24# KanonInD
  1. #include <stdio.h>

  2. int main()
  3. {
  4.   float f = 1.0/3.0*3.0;
  5.   printf("%.10f\n", f);
  6. }
复制代码
以下是相应由gcc产生的汇编代码,注意看LC0, LC0=1065353216, 1065353216的16进制表示是3f800000, 即浮点数1.0的表示。

(/ a a) -> 1.0
  1.         .file        "test.c"
  2.         .section        .rodata
  3. .LC1:
  4.         .string        "%.10f\n"
  5.         .text
  6.         .globl        main
  7.         .type        main, @function
  8. main:
  9. .LFB0:
  10.         .cfi_startproc
  11.         pushl        %ebp
  12.         .cfi_def_cfa_offset 8
  13.         .cfi_offset 5, -8
  14.         movl        %esp, %ebp
  15.         .cfi_def_cfa_register 5
  16.         andl        $-16, %esp
  17.         subl        $32, %esp
  18.         movl        .LC0, %eax
  19.         movl        %eax, 28(%esp)
  20.         flds        28(%esp)
  21.         fstpl        4(%esp)
  22.         movl        $.LC1, (%esp)
  23.         call        printf
  24.         leave
  25.         .cfi_restore 5
  26.         .cfi_def_cfa 4, 4
  27.         ret
  28.         .cfi_endproc
  29. .LFE0:
  30.         .size        main, .-main
  31.         .section        .rodata
  32.         .align 4
  33. .LC0:
  34.         .long        1065353216
  35.         .ident        "GCC: (GNU) 4.7.1 20120721 (prerelease)"
  36.         .section        .note.GNU-stack,"",@progbits
复制代码
补充:以下规则对编译器应当没压力才对。
(/ (* a b) b) -> a
(* (/ a b) b) -> a
(* a 1.0) -> a
(* 1.0 a) -> a

论坛徽章:
1
丑牛
日期:2013-09-29 19:04:50
28 [报告]
发表于 2012-08-25 15:58 |只看该作者
本帖最后由 solu 于 2012-08-25 16:00 编辑
x5miao 发表于 2012-08-25 00:30
回复 23# solu

static int i = 2 || 1 / 0;
编译时常量表达式会被直接替换成常量。运行时根本就没计算1.0/3.0的这个过程,懂不?



有没有这个过程,跟楼主讨论的问题有屁关系没有? 你以为我们说的四舍五入是在1.0/3.0这个阶段吗?

你以为就你知道常量表达式? 就你知道常量表达式存储在常量区?



常量你妹啊

论坛徽章:
0
29 [报告]
发表于 2012-08-25 16:59 |只看该作者
我分析输出1.000000的原因可能是:
1. 1.0/3.0 在没*3之前是浮点类型;
2. 1.0/3.0*3这时由浮点转成整型,四舍五入,此时的值是 =1;
3. 但是左值声明的是float,在赋值完成后整型被转成浮点,此时1转型为1.000000。

论坛徽章:
0
30 [报告]
发表于 2012-08-25 23:59 |只看该作者
回复 27# KanonInD


    你的这些规则相对于编译器其他优化来说的确是轻而易举的事情。但是常量折叠技术已经有教科书式的算法,通过加入这些规则来避免编译期计算仅仅影响编译速度,不影响结果。而匹配规则条件的复杂度和编译期模拟计算复杂度似乎差不了多少(本人认为后则似乎更简单),而基于编译期计算的常量折叠优化很明显适用范围比列举的那些规则宽泛得多得多,而且更容易实现。

更重要的,针对一般情况的常量折叠是所有编译期必备的优化步骤,基于规则识别的优化不能比它更强大的话,那就是多余的。

附上一种常量折叠优化思路:

foreach (basic_block bb in control flow)
    foreach (instruction i in bb)
        if ( i is "assignment" && i.rhs1.value is determined && i.rhs2.value is determined) // assignment : lhs = rhs1 @ rhs2
            i.lhs.value = i.rhs1.value @ i.rhs2.value; // 编译期计算出值

// 生成指令的时候,lhs.value如果有确定值,那就直接使用它,这条assignment指令可以删除掉,
// 还有附加效果是:可能会导致之前与rhs1和rhs2相关指令成为dead指令,这些指令将在后续优化中被清除。


这里只考虑了基本块内的情况,跨基本块的情况复杂一些,还依赖于跨基本块的变量扫描,扫描出的信息可供很多其他优化使用,比如复制传播。

所以优化和浮点数的舍入是两个独立的问题。如果有了优化就不存在浮点数舍入的话,除非编译器懂得在适当的时候运用结合律、分配律和交换律,可能吗?

比如 1.0/(1.0+1.0+1.0)*1.5*2.0
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP