printf打印float错误引起的思考2008-09-12 09:58:59 来源:不详 作者:转载
昨天跟一个朋友讨论一个问题:float n1=3.0; double n2=3.0; long n3=2000000000; long n4=1234567890; printf("%ld %ld %ld %ld\n", n1, n2, n3, n4); 在x86 32位机上执行,输出结果是:0 1074266112 0 1074266112 按照我的理解,float和long在32位机下都占4个字节,即使无法格式化为long进行输出,后面的值也不至于都出问题。 实际里面涉及以下的知识, 1. 把上面的代码反汇编可以看到: mov 0x80494b4,%eax mov %eax,0x18(%esp) mov 0x80494b0,%eax mov %eax,0x14(%esp) fldl 0x80494a8 fstpl 0xc(%esp) flds 0x80494a0 fstpl 0x4(%esp) movl $0x8048478,(%esp) call 0x804828c <printf> 我先后把这几个变量定义成了局部和全局的进行了测试,这段是用全局的汇编码。 关键是这几句: fldl 0x80494a8 fstpl 0xc(%esp) flds 0x80494a0 fstpl 0x4(%esp) fldl把浮点地址交给浮点运算寄存器,浮点寄存器是64为精度的,然后fstpl再把值取出按照64位入栈,表象就是按照double类型入栈了 。所以虽然内存中单精度浮点数是以4字节存储的,但编译器把它转成了8字节进行处理。 所以最后在栈里的存储就成了 | 00 00 00 00 |<--ESP | 40 40 00 00 | | 00 00 00 00 | | 40 40 00 00 | | 49 96 02 d2 | | 77 35 94 00 |<--EBP 2. printf的处理方式是将输入参数按照字符串进行格式化解析,由于输入的是ld ld ld ld,按照4字节依次进行取值,就得到了上面的结果。 3. float是单精度浮点,遵从IEEE754, S(1)-E(8)-M(23)。3二进制位11,表示为:1.1×2^1 => 指数部分127+1=128- >10000000,小数部分为1,因此其浮点表示为: 0 10000000 10000000000000000000000,也就是上面1中看到的0x40400000,打印出的十进制就是1074266112。 后来又找来台cavium64位的机器上跑了一下,发现都是正确的,即使按照%d方式输出也没有问题,没法在cavium上反汇编无法确认。
|
|
||||
|
|
||||
|
|
|
||||
|
|
||||
|
|
|
||||
|
|
||||
|
|