背景:浮点存储方式
这里以6.1为例。
例如:
小数 | 输出对应二进制字符串代码(前面的0不输出) | 二进制输出(符号位+指数位+尾数位) |
---|---|---|
6.1(float) | Integer.toBinaryString(Float.floatToIntBits(6.1f)) | 0 10000001 10000110011001100110011 |
6.1(double) | Long.toBinaryString(Double.doubleToLongBits(6.1)) | 0 10000000001 1000011001100110011001100110011001100110011001100110 |
接下来,恭喜 ----- 全是提问~~ 别急着走,先往下看~
浮点指数位范围疑问
浮点中指数位以阶码形式存储,float 指数范围是 -128 ~ 127;
规定:-128的补码是1000 0000。
原码 +0 与 -0 转换为 阶码 是 0111 1111,
因 -128的补码为1000 0000,则转换为 阶码 也是 0111 1111?
这样明显不对,-128的阶码应该有别的数。。
从下表可以发现:
阶码为 1111 1111的值被闲置了(-128 ~ 127都没有值与其对应),
阶码为 1111 1111 的数难道就是 -128? 这是规定吗?如果是规定,如果是,则 -128的补码按规则转换成阶码方式是错误的?
十进制 | 二进制原码 | 二进制阶码 |
---|---|---|
-128 | 0111 1111? | |
-127 | 1111 1111 (补码1000 0001) | 0000 0000 |
-1 | 1000 0001(补码1111 1111) | 0111 1110 |
-0 | 1000 0000 | 0111 1111 |
+0 | 0000 0000 | 0111 1111 |
1 | 0000 0001 | 1000 0000 |
2 | 0000 0010 | 1000 0001 |
127 | 0111 1111 | 1111 1110 |
浮点输出疑问
疑问一:
*6.1f 存储的是 0 10000001 10000110011001100110011,转换成十进制只能是6.099999904632568,下面的代码为何却能正常打印成 6.1;
两个float类型相加也没有丢失精度,why?
这里有需要借助汇编来理解的必要吗?
float shoultScore = 6.1f;
System.out.println(shoultScore);// 输出 6.1
//两个6.1f相加得到的二进制应该是 1100.001100110011001100110应该是输出12.199999809265137
System.out.println(6.1f+6.1f); // 输出 12.2
System.out.println(6.1f*2); // 输出 12.2
//下面这个打印正常,float6.1和double6.1存储后的二进制相加即可得到该值
System.out.println(6.1f+6.1); // 输出 12.199999904632568
//以下这样输出float6.1 能得到预期的值
BigDecimal bd = new BigDecimal(shoultScore);
System.out.println("BigDecimal : " + bd);
疑问二:
如下输出,为何float与double相乘就丢失进度了,,同类型相乘能正常显示。。。是因为同类型二进制计算后的结果换算成十进制恰巧就是 305吗反之就得到304.9999952316284?
System.out.println("6.1f * 0.5f * 100 = " + 6.1f*0.5f*100); //6.1f * 0.5f * 100 = 305.0
System.out.println("6.1f * 0.5 * 100 = " + 6.1f*0.5f*100); //6.1f * 0.5 * 100 = 304.9999952316284
System.out.println("6.1 * 0.5 * 100 = " + 6.1*0.5*100); //6.1 * 0.5 * 100 = 305.0
浮点反编译疑问
通过反编译工具 jad 编译 double i = 6.1;得到的结果是
double i = 6.0999999999999996D;
而 javap中是 6.1D。 float类型就不存在这个问题