Java float和double存储结构

Java浮点数存储结构

浮点数转换二进制

18.725为例进行说明。

整数部分转二进制:除2取余法。整数部分除以2取余数,商取整继续除以2取余数,直到商为0。

整数除法 余数
$18/2$ 9 0
$9/2$ 4 1
$4/2$ 2 0
$2/2$ 1 0
$1/2$ 0 1

18转换为二进制就是10010,上面表格中余数的倒序。

小数部分转二进制:乘2取整法。小数部分乘以2,取整数部分,剩下的小数部分继续乘以2取整数部分,直到结果为0。如果永远不为零,则到达期望的精度后终止运算。

乘法公式 小数部分 整数部分
$0.725*2=1.45$ 0.45 1
$0.45*2=0.9$ 0.9 0
$0.9*2=1.8$ 0.8 1
$0.8*2=1.6$ 0.6 1
$0.6*2=1.2$ 0.2 1
$0.2*2=0.4$ 0.4 0
$0.4*2=0.8$ 0.8 0
$0.8*2=1.6$ 0.6 1
$0.6*2=1.2$ 0.2 1
$0.2*2=0.4$ 0.4 0
…… …… ……

0.725的二进制数是0.1011100110(后面还有无限长,这里忽略)。所以18.725的二级制就是10010.1011100110。按照规定,二进制小数点左边只能有1为且固定为1,所以需要进行右移操作,得出结果是:$1.00101011100110*2^4$

  • 符号位:正数,符号位为0;
  • 指数位:实际为4,按照规定要加上127(即指数最高位赋值为1),指数位存储的是131,二级制为10000011,。
  • 底数位:取小数部分,即 0.00101011100110

为什么指数位要加上127呢?这是因为float类型指数位是8位,取值范围位-126~127,为了消除负数带来的实际计算上的影响(比如比较大小,加减法等),可以在实际存储的时候,给指数做一个简单的映射,加上一个偏移量,比如float的指数偏移量为 127,这样就不会有负数出现了。另外double类型是加上1024。

18.725存储的float二进制数据是:0 10000011 00101011100110...

可以通过下面代码进行验证:

1
2
3
4
5
System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(18.725f)));
//输出结果位:0 10000011 00101011100110011001101(高位0实际上没有输出,这里手动补上了)

System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(18.725)));
//输出结果:0 10000000011 0010101110011001100110011001100110011001100110011010

字符串转浮点数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//本示例是最基础的转换,比如输入“34.5789”,输出为 34.5789
public static float stringToFloat(String str) {
int dotIndex = str.indexOf(".");
if (dotIndex < 0) {
dotIndex = str.length();
}
float value = 0f;
for (int i=dotIndex-1, j=0; i>=0; i--, j++) {
value += (str.charAt(i)-'0') * Math.pow(10, j);
}
for (int i=dotIndex+1, j=1; i<str.length(); i++, j++) {
value += (str.charAt(i)-'0') * Math.pow(10, -j);
}
return value;
}

参考文章