先看这个小程序会打印出什么值:
package net.villim.www;
public class TestFloat {
public static void main(String[] args) {
System.out.println(2.0 - 1.1);
}
}
当然不会那么简单,绝对不可能是 0.9!!
实际打印出的值是 0.8999999999999999 确实让人大跌眼镜。发什么什么事情?
———————有兴趣,请自己先想想——————————
##为什么不能正确打印 0.9 ??
这里的猫腻当然就是 System.out.println(2.0 - 1.1); 简简单单的 “2.0”和 “1.1” 有什么问题呢?
原因就是计算机无法准确的描述 “1.1” 这个值! 我们知道计算机最终是用2进制来运算的。所以我们需要将 1.1 转化为二进制。
###十进制整数转二进制整数:
采用”除2取余,逆序排列”法。具体做法是:用2去除十进制整数,可以得到一个商和余数;再用2去除商,又会得到一个商和余数,如此进行,直到商为一时为止,然后把先得到的余数作为二进制数的低位有效位,后得到的余数作为二进制数的高位有效位,依次排列起来。
###十进制小数转换成二进制小数:
采用”乘2取整,顺序排列”法。具体做法是:用2乘十进制小数,可以得到积,将积的整数部分取出,再用2乘余下的小数部分,又得到一个积,再将积的整数部分取出,如此进行,直到积中的整数部分为零,或者整数部分为1,此时1位二进制的最后一位。或者达到所要求的精度为止。
所以,假如我们用 8 位来表示 “1.1” 可以得到 “0000 0001 . 00011001” . 我们再将二进制转为十进制,得到的是 “1.09765625” !! 就是说计算机只能得到最接近 1.1 的值。更准确的描述应该是,不是所有的小数都能精确地用二进制浮点数表示。
所以当我们需要精确使用小数的时候,怎么办呢? 应该使用 int,long 或者 BigDecimal
使用 BigDecimal 的时候也要格外注意,应该使用 BigDecimal(String) 而不是BigDecimal(double)!
假如尝试打印 BigDecimal(0.1) 你将得到 0.1000000000000000055511151231257827021181583404541015625!! Orz ~~
###快速转二进制为十进制
比如 0.635 ,如何判断需要多少位才能精确地表示它呢?
根据前面的转换规则其实我们可以推断出简单的方法: 就用那个十进制乘以2的n次方,看小数部分是否为零,是的话就能用n位精确表示,否的话就不能。 比如,0.635256=162.56。因为小数部分不为0,所以8位二进制小数无法精确表示。再看0.63565536=41615.36。小数部分仍不为0,所以16位也不能精确表示。
如何快速转换呢? 比如 52.63 , 如果用8位表示小数部分,把0.63*256也就是2的8次方得到161.23,23不要,把161化成二进制表示为 10100001,再连上52的,就成了00110010.10100001。
###这个问题还有一个引申的关注点,计算机是如何做这个减法的呢?
这里有三个老概念,原码,补码和反码。
以下用 X1 = + 1010 110 和 X2 = -1001010 来举例。
原码 (True Form):其符号位用0表示正号,用1表示负号,数值一般用二进制形式表示。
[X1]原=[+1010110]原=01010110 [X2]原=[-1001010]原=11001010
在原码表示法中,对0有两种表示形式:
[+0]原=00000000 [-0] 原=10000000
补码 (2’s complement): 补码可由原码得到。如果机器数是正数,则该机器数的补码与原码一样;如果机器数是负数,则该机器数的补码是对它的原码(除符号位外)各位取反,并在未位加1而得到的。
[X2] 原= 11001010 [X2] 补=10110101+1=10110110
在补码表示法中,0只有一种表示形式:
[+0]补=00000000 [+0]补=11111111+1=00000000(由于受设备字长的限制,最后的进位丢失)
反码 (1’s complement): 反码可由原码得到。如果机器数是正数,则该机器数的反码与原码一样;如果机器数是负数,则该机器数的反码是对它的原码(符号位除外)各位取反而得到的。
[X1]反=[X1]原=01010110 [X2]反=10110101
在计算机系统里,数值一律用补码来表示(存储)。 主要原因:使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补 码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。