浮点数在内存中的存储
根据IEEE standard 754,float用4个字节存储,double用8个字节存储。float 1个bit用来存储浮点数的符号,8个bit用来存储浮点数的指数,23个bit用来存储浮点数的尾数。对于double 1个bit用来存储浮点数的符号,11个bit用来存储浮点数的指数,52个bit用来存储尾数。
Sign | Exponent | Fraction | |
Single Precision | 1[31] | 8[30-23] | 23[22-0] |
Double Precision | 1[63] | 11[62-52] | 52[51-0] |
符号位:0表示正,1表示负
指数位:指数位既要能够表示出来正的指数同时也要能够表示出来负的指数。在设计上,为了存储,在实际的指数上加了一个偏移(bias)。对于IEEE 单精度的float为例,这个值是127。这样对于指数位为0,实际在指数位中存储的是127.对于指数位中存储的是200的指数,实际存储的是73(200-127)。对于指数位全是0和全是1的情况有特殊表示,下面会讨论到。
尾数:对于一个10进制的浮点数转化为科学计数法的2进制的形式都可以表示为1.f*,其中f表示的是二进制的尾数,例如3.5可以表示为1.11*
。在存储上只需要存储f既可以,因为小数点前面永远都是1,这样可以省下来一个bit。
浮点数的精度
对于一个单精度的浮点数:
11110000 11110000 10100000 10101111 32-bit Integer
= +1.1110000 11110000 10100000 10101111 * Full-Precision Float
= +1.1110000 11110000 10100000 * Single-Precision Float
= 11110000 11110000 10100000 00000000 Corresponding Value
从上面的例子可以看出对于单精度浮点数只能够表示一定精度范围内的数值,对于超出其精度的部分会做截断。在牺牲一定精度的基础上,对于同样是32bit,单精度浮点数能够表示的数字可以达到的范围,而对于整型只能够表示到
.
特殊的值
0
我们知道对于使用科学计数法的二进制数,小数点的前一位永远是1,那么这种方式永远也无法表示出来0。在IEEE standard 754中将0作为一个特殊的值进行处理。0是将指数位和尾数位全部置为0,但是有+0和-0之分,但是他们是相等的(+0 == -0)。
Denormalized
对于一个指数位都是0,但是尾数不是0的浮点数(如果尾数也都是0,则是上面说到的0的表示),那么这种数是一种非规格化的数字。这种情况下,对于使用科学计数法的二进制数的小数点前面的1不再是1,而是0。这个时候的单精度浮点数表示为 * 0.f *
Infinity
对于指数全是1,而尾数都是0的情况用来表示无穷。符号位为0表示正无穷,符号位为1表示负无穷。IEEE支持浮点数和无穷的各种比较计算。
Not a Number
对于指数都是1,但是尾数不是1的情况用来表示NaN(Not a Number),用来表示一些在数学上的非法定义。
特殊的计算
IEEE对特殊的值的计算有明确的定义,如下表所示。
浮点数的比较
在前面已经说过,浮点数只能够在一定精度上表示数字,所以对于真实的数字可能存在rounding error(舍入误差)。对于两个浮点数它在计算机中的表示不存在舍入误差,那么它可以使用“==”和"!="进行比较(即看4bytes所存储的bit是否完全相同),而对于存在舍入误差的两个数,就要考虑在多少误差范围之内两个数是否相同,例如:
if(Math.abs(a-b) < Precision){ ... }