# 十进制小数用二进制如何表示
在小数转换为二进制时候,整数部分通过除以 2 递归的方式转为二进制表示,而小数部分可以通过乘 2 方式转换为二进制,如下所示
# 3.5转换为二进制 | |
3 / 2 = 1 --- 1 | |
1 / 2 = 0 --- 1 | |
所以整数部分为11 | |
0.5 * 2 = 1.0 -- 1 | |
因此3.5的二进制表示就为 11.1 | |
# 把0.1转换为二进制 | |
0.1 * 2 = 0.2 ---- 0 | |
0.2 * 2 = 0.4 ---- 0 | |
0.4 * 2 = 0.8 ---- 0 | |
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.6 * 2 = 1.2 ---- 1 | |
0.2 * 2 = 0.4 ---- 0 | |
......... 按照0011无限循环下去 |
因此可以知道将一个十进制的小数转化为二进制小数时候可能得到一个无限循环小数,这也是部分计算丢失精度的原因之一。
# Java 中怎么保存小数
在 Java 中是遵守 IEEE-754 格式标准来保存的。也就是一个二进制小数使用科学计数法表示,然后将这个表达式保存起来。 符号位+指数位+小数位
在 java 中通常由单精度浮点数 float(32 位)和双精度浮点数 double(64 位)。在 float 中的格式 1 位符号位,8 位指数,23 位表示小数;
在 double 中 1 位符号位,11 位指数位,52 位小数位。因为双精度能保存更多的小数位,这也就是它精度更高的原因。
3.5使用float保存如下1.11E+1 | |
0(符号位) 10000000(1+127,此处有一个指数偏移) 11000000000000000000000(由于一定是1.xx的一个数所以前面的1会保存) |
# 为什么经过计算的小数会打印错误而直接打印就没问题
System.out.println(0.3); # 0.3 | |
System.out.println(0.1 + 0.2); # 0.30000000000000004 | |
System.out.println(0.30000000000000002); # 0.30000000000000004 | |
System.out.println(0.30000000000000001); # 0.3 |
推测是 print 函数对其做了输出的优化,实际上还是会有精度丢失问题,只是恰好优化成了正确结果。