目录
起因
在阅读《阿里巴巴开发手册》时发现了一句话:
【强制】浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用equals来判断。
说明:浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。二进制无法精确表示大部分的十进制小数,具体原理参考《码出高效》 。
结论
浮点数有精度问题,不适用于比较大小或比较相等性的逻辑;
科学计数法
假如一个数字:1234,用科学计数法来表示就是:1.234 * 10^3,其中10叫做基数,3叫做指数/阶码/阶,1.234叫做尾数;
因此,假如在某个系统中,基数是固定的,那么只需计算尾数、指数即可,而计算机系统,针对浮点数就是采用这种方式,其大致流程:
①先把十进制数字转换成二进制数字;
②再把二进制数字转换成二进制科学计数法;
③尾数必须以1开头;
十进制转二进制
整数
基本
由于是二进制,因此,十进制整数转二进制则是除以2,余数则是按照从低位往高位依次排序,举个例子,十进制数字11,转二进制:
第一次11除以2,商5,余1 | 这次把最低位筛选出来,最低位不足2进位 |
第二次5除以2,商2,余1 | 把次高位筛选出来 |
第三次2除以2,商1,余0 | 把次高位筛选出来 |
第四次1除以2,商0,余1 | 把次高位筛选出来 |
十进制转二进制,就是不断除以2,每一次筛选出低位,一直筛选到最高位,直到0除以2商为0出现;
二进制科学计数法
十进制:11
二进制:1011
二进制科学计数法:1.011 * 2^3
总结
用是否除以2是否有余数来分类,整数分为2大类:偶数、奇数;
因此,奇数无非就是最低位为2^0而已,因此,无论整数多大,也无论是偶数还是奇数,二进制均可以精确表达;
小数
基本
二进制整数取值是:1-2-4-6-8-...-正无穷大,因为是2进制,高位是低位的2倍,从低位到高位依次为:1-2-4-8-16-正无穷,所以,整数唯一能确定的就是最低位;
二进制小数就有点尴尬,由于高位是低位的2位,从高位到低位依次为:0.5-0.25-0.125-正无穷小,所以,小数唯一能确定的就是最高位,这里有个问题,小于最高位只能表示为0.5,那0.1/0.2/0.3/0.4/0.6/0.7/0.8/0.9这类小数转3二进制的时候就会出现死循环的情况,例如:
以0.9举例来说明:
0.9*2=1.8,取整数部分1 |
|
0.8*2=1.6,取整数部分1 |
|
0.6*2=1.2,取整数部分1 |
|
0.2*2=0.4,取整数部分0 |
|
0.4*2=0.8,取整数部分0 |
|
0.8*2=1.6,取整数部分1 |
|
又是下一轮循环 |
|
那十进制0.9转二进制,约为:0.111001
二进制科学计数法
十进制:0.9
二进制:0.111001
二进制的科学计数法:1.11001 * 2^(-1)
总结
小数部分是从0.5除以2开始,所以,像是0.1/0.2/0.3/0.4/0.6/0.7/0.8/0.9根本没法精确表达,因此,小数在计算机中存储的时候,会涉及到精度损失问题;
IEEE754标准存储
基本
JAVA中float占4个字节,共32位,其存储遵循IEEE 754标准:
第31位:符号位,正负号,负数时为1,正数时为0;
第30位:指数符号位,为正为1,否则为0;
第23-29位:指数位,二进制科学计数法指数部分加上偏移量的二进制表示;
第0-22位:尾数部分,只取小数点后面的尾数,因为前面肯定是1,不需要保存;