C语言中浮点数精度问题
参考:
IEEE_754 http://zh.wikipedia.org/zh-cn/IEEE_754
剖析Intel IA32架构下C语言及CPU浮点数机制,http://blog.csdn.net/xiaohan13916830/article/details/99038
浅谈计算机中浮点数的表达方法(IEEE 754) http://zhan.renren.com/programming4idiots?gid=3602888498026486936
printf打印64位整数导致的bug,和宽度指定符需要注意的地方 http://hi.baidu.com/widebright/item/523ea0c8cec4ebd4964452dd
浮点数 %d 输出 http://www.cnblogs.com/yangyh/archive/2011/10/03/2198631.html
这里是一篇关于浮点数的详细讲解 http://bbs.bccn.net/thread-44500-1-1.html
C语言中浮点数的格式采用的是IEEE 754标准,当理解这个标准之后,C语言中好多奇怪的现象便可以解释了。
一、IEEE 754
标准规定表达浮点数的0、1序列被分为三部分

符号位sign表示数的正负(0为正,1为负),指数exponent表示科学计数法的指数部分,分数fraction表示尾数部分
以32位单精度浮点数为例:125.125D = 1111101.001B = 1.111101001*2^6
则符号位为0, 尾数部分为11110100100000000000000(共23位),蓝色部分为上例科学计数法中的小数部分,不足位数的补零, 指数部分6+2^(8-1)-1=133 即 10000101(8位),这里所填的指数并不是前面算出来的实际指数,而是等于实际指数加上一个数(指数偏移),偏移量为2^(e-1)-1,其中e是exponent的宽度(位数),32位单精度浮点数exponent宽度为8。
那么32位浮点数125.125D在计算机中就被表示为01000010111110100100000000000000。
因此
对于32位单精度浮点数,sign是1位,exponent是8位(指数偏移量是127),fraction是23位。
对于64位双精度浮点数,sign是1为,exponent是11位(指数偏移量是1023),fraction是52位。
实际上,上述的的浮点数是规约形式的浮点数,即科学计数法中整数部分为1,指数部分 1 < exponent < 2^(e-1)。而如果科学计数法中整数部分为0,浮点数的指数部分的编码值是0,尾数为非零,那么这个浮点数将被称为非规约形式的浮点数。IEEE 754标准规定:非规约形式的浮点数的指数偏移值比规约形式的浮点数的指数偏移值大1.
比如32位单精度浮点数最小的正非规约形式的浮点数 0 0000 0000 000 0000 0000 0000 0000 0001 对应的二进制科学计数法计算如下:
符号位为0表示正数,指数位为0,表明为非规约形式的浮点数,则科学计数法中的指数部分计算,按照规定即为 0 - 127 + 1 = -126
即对应的二进制科学计数法为 0.00000000000000000000001× 2−126 = 2−23 × 2−126 = 2−149 ≈ 1.4×10-45
同理64位双精度浮点数的表示方法,亦然。
二、C语言中%d格式输出浮点数的问题
32位机器上,C语言中int类型占 4个字节,即32位。那么以%d格式输出浮点数,必然只有其中的32位。
为什么不是高32位,或者低32位,抑或是随机的32位?这是与CPU的浮点数处理机制有关。Intel CPU浮点处理单元的控制寄存器中最重要的就是它指定了这个浮点处理单元的舍入的方式及精度(24位,53位,64位)。Intel CPU浮点处理器的默认精度是64位,而24位,与53位的精度,是为了支持IEEE所定义的浮点标准(IEEE 754标准),也就是C语言中的float与double。这与IEEE 754标准时兼容的。在CPU的内部,所有的浮点数的在被浮点指令装入浮点寄存器的时候都会发生转换,从单精度、双精度、整数转换为80位的扩展精度,当从浮点寄存器存入内存的时候又会发生转换,从扩展精度转换为相应的精度格式。这种转换是由CPU硬件自动完成的,然而正是由于从扩展精度转换为低精度格式这一行为的存在,会让我们的程序出现某些很奇怪的问题。
如:printf(“%d”,5.1F);调用这句代码时发生了什么?
单精度浮点数:5.1F = 101.000110011001100110011..(有效位写满23位) = 1.0100011001100… * 2^2
单精度 0100 0000 1010 0011 0011 0011 0011 0011
双精度,有效位不足补0,0 100 0000 0001 0100 0110 0110 0110 0110 0110 0000 * 7
十六进制 0 1025 40146666 60000000
故输出低32位(0X60000000)所表示的十进制数:1610612736
单精度 0100 0000 1010 0011 0011 0011 0011 0011
双精度,有效位不足补0,0 100 0000 0001 0100 0110 0110 0110 0110 0110 0000 * 7
十六进制 0 1025 40146666 60000000
0为第1位, 1025为 2-11位 40146666为 12-32位 60000000 为后32位

浙公网安备 33010602011771号