先引入一道题:
Float与“零值”如何比较?
当然不能用
if(f == 0)
之类的比较~!
参考答案:
Const float EPSINON = 0.00001;
If((x >= -EPSINON)&&(x <=EPSINON))
首先,他提醒了我起名字问题,这个词儿还真没怎么用过,为了更专业,我们还是多学俩但词儿吧,那么,看看EPSINON含义——其实,他拼错了,是episilon,e么,指数么,上大学肯定都有印象,各理工科经常提及的希腊字母:
Ε ε epsilon ep`silon 伊普西龙
其次,浮点数,比如float,有二十几位的真值,我比小数点后几位合适?太多位的话会不会出差错?他本来就不是严格的“0”~!
又想起来一个问题,float double等等,反正就是32位,64位(还有更高么?)等等,具体分布,比如第一位是符号位,然后五六位是指数级,其余是“真值”——我该怎么叫他来着。。
其实这个题没什么好分析的,只是引入一下浮点数的概念,下边自己编了一个小程序:
输出:
1.234000
address of f1 is 0xbfa00510,and the value is 1.234000
首先,给结构体f1中的f1变量赋值并打印输出来测试实际值
address of fp1 is 0xbfa00510,and the value is 1.234000
然后,是“脱了裤子放屁”,把结构体强制转换给浮点指针,因为结构体里存的也是一个浮点数,地址又相同,所以输出结果是一样的。
address of cp1 is 0xbfa00510,and the value is ffffffb6
address of cp1 + 1 is 0xbfa00511,and the value is fffffff3
address of cp1 + 2 is 0xbfa00512,and the value is ffffff9d
address of cp1 + 3 is 0xbfa00513,and the value is 3f
然后就是拆解了,通过下方的int和上方的char打印结果,可以看到f1.f1在内存中实际存储的样子(小端存储,低地址存放低位值),打印char类型会自动在前边补1(化成16进制,假如足够4位,即一个 f)
address of ip1 is 0xbfa00510,and the value is 3f9df3b6
那么这个0x3f9df3b6和1.234000 有什么关系呢?
先拆成2进制看看。
按地址,大--->小:
3 f 9 d f 3 b 6
0011 1111 1001 1101 1111 0011 1011 0110
这个就很抽象了~!
那怎么解释呢,这就要提到浮点数在计算机中的储存格式了。如果说通常的整型更符合我们对于数据本该有的形式。浮点型更像是一个组合结构体,位结构体,这也就是我在代码中使用了一个struct的原因(只是没有真的按浮点数格式去按位拆解)。
提到浮点数,就想起《计算机组成原理》,就不得不提一个标准,IEEE754(我们现在用的是什么呢?就是IEEE754~~!当然,只有32位模式有强制要求,其他都是选择性的。大部分编程语言都有提供IEEE浮点数格式与算术,但有些将其列为非必需的。例如,IEEE754问世之前就有的C语言,现在有包括IEEE算术,但不算作强制要求(C语言的float通常是指IEEE单精确度,而double是指双精确度)。)
IEEE 754规定了四种表示浮点数值的方式:单精确度(32位)、双精确度(64位)、延伸单精确度(43比特以上,很少使用)与延伸双精确度(79比特以上,通常以80比特实做)。
各精度的位数分配分别为:
单精度:总共32位,最大1位是符号位,其次8位是幂指数,然后就是23位的真值。单精度的指数部分是?126~+127加上偏移值127,指数值的大小从1~254(0和255是特殊值)。
双精度:总共64位,最大1位是符号位,其次11位是幂指数,然后就是52位的真值。双精度的指数部分是?1022~+1023加上1023,指数值的大小从1~2046(0(2进位全为0)和2047(2进位全为1)是特殊值)。
长精度:总共80位,忘记了,真值大概六十几位,也不常见。。。
单精度、双精度、强调一个精度嘛,主要还是真值,指数增加的不是很多(因为也没必要那么大了,太大了)。
先看一下我们用的,按理说是IEEE754单精度,来分析一下;
float总共32位,最大1位是符号位,其次8位是幂指数,然后就是23位的真值。
PS:浮点数的指数还不“直接”是指数,应该是移码。
8位,移码,127+127=254,254为2的八次幂,超了。最大为127次幂
最小为-127次幂,加上127为0.
那么我们来算算例子中是不是如此:
0011 1111 1001 1101 1111 0011 1011 0110
0 01111111 00111011111001110110110
该浮点数为正数,阶码为01111111,尾数部分为00111011111001110110110。因为在IEEE754中,单精度浮点数有规格化处理,所以其真正尾数部分为1.00111011111001110110110,其中‘.’为小数点。
阶码移码的01111111表示0(127-0 = 127),相当于没偏移(就是1.234嘛),所以尾数部分的小数点不用移动即可得到真实的数据。
真实数据就是:1.00111011111001110110110,其十进制值为:
1 + 1/8 + 1/16 + 1/32 + 1/128 + 1/256 + 1/512 + 1/1024 + 1/2048 + 1/2^14 + 1/2^15 + 1/2^16 + 1/2^18 + 1/2^19 + 1/2^21 + 1/2^22 ~=1.234
虽然顺手编了这个个不具代表性的浮点数——1.234,完全不用偏移,但是原理是一样的,到时候只要按偏移位数移动小数点,就能算出所需值了。
举个通俗易懂的例子,一个数,表示大于0的23位数,又一个数是小数点后22位,你如何进行他们的减法?“换算呗”。但是计算机不是人,他没有笔和纸,只有寄存器。没有办法换算成功,让你乘上幂数,使两数达到同一“数量级”,因为整数部分23位,小数部分22位,加一块45位了,假如是32位的计算单元(寄存器),明显不能把他们放进去进行无损精度的减法,so...
当然,我不是说浮点数计算就没办法了,我只是说计算机的底层实现能力有限,你在上层通过一些技巧,在高级语言部分,总有办法让他无损计算的,只不过是慢点,效率低点。
原文地址:http://blog.csdn.net/huqinwei987/article/details/24784857