前提:
- 在计算机中数据是以二进制的方式存储
- 浮点数使用科学计数法的方式来表示
存储方式
浮点数的存储主要分为 3 个部分:
- 阶符:Sign,1 位,0 表示正,1 表示负
- 阶码:Exponent,表示指数大小,
- 尾数:Mantissa,数值部分
浮点数的值:S * M * 3^E
float:4Byte = 32bit,S 占用 1bit,E 占用 8bit,M 占用 23bit
double:8Byte = 64bit,S 占用 1bit,E 占用 11bit,M 占用 52bit
转换
举例,将 1 个 float 数据转换为 4Byte 的二进制数据存储起来:
float a = 10.25F;
Decimal Binary
整数部分: 10 ====> 1010
小数部分: 0.25 ====> 0.01
科学计数法: 1.025 * 10^1 ====> 1010.01 = 1.01001 * 2^3
S = 0 ====> 0
E = 3 ====> 计算机使用 0111 1111 = 2^7 - 1 = 127 来表示 E = 0,所以当 E = 3 时,二进制表示为: 1000 0010 = 130
M = 1.01001 ====> 尾数为规格数,小数点前都是 1,所以直接去掉,只保留小数部分,二进制表示为:0000 1001
S E M
10.25 ====> 0 | 1000 0010 | 00000000000000000001001
====> 0100 0001 0000 0000 0000 0000 0000 1001
需要注意的 3 个地方:
小数部分:0.25 = 1/4,即 2 的 -2 次方,也就是二进制的 1 小数点左移 2 位,所以得到的二进制表示为 0.01。需要注意的是:不是所有的十进制小数都能转换为二进制小数,比如十进制的 0.3,所以只能取近似值,通过 2^-n(n>0,n 为整数) 来接近 0.3
规格数:基数为 2,位数最高位为 1 的数为规格数,此时能够表示的数据精度最高。通过阶码的大小来控制小数点的位置使得尾数变为规格数的过程,称为规格化。在存储时尾数只保存了小数部分。但是规格数无法表示 0,这一点需要注意
阶码是有偏移量的,以 2^7 - 1 为基准,在转化或者解析时需要加上或者减去 127
特殊值
- 0:以非规格化的方式存储
- 无限大:阶码全为 1,尾数全为 0 表示 Infinity
- NaN:Not a Number,不是一个可以表达的数,用阶码全为 1,尾数不等于 0 来表示
Java 中的应用
将给定的字节数组转换为对应的浮点数,JDK 中 Float 和 Double 均提供了对应的方法来处理这种情况,以 Float 为例:
public static float intBitsToFloat(int bits)
将给定的 4 个字节转换为 int 类型,然后计算 32bit 所表示的浮点数,API 中的说明:
int s = ((bits >> 31) == 0) ? 1 : -1;
int e = ((bits >> 23) & 0xff);
int m = (e == 0) ?
(bits & 0x7fffff) << 1 :
(bits & 0x7fffff) | 0x800000;
浮点结果等于算术表达式 s·m·2e-150 的值
对应给定的浮点数,进行四舍五入可以使用 BigDecimal.setScale()方法,可以设定保留小数的位数,以及四舍五入的方式
参考
- 浮点数在计算机中存储方式 - Robin Zhang - 博客园
- 深入理解计算机系统(2.7)---二进制浮点数,IEEE标准(重要) - 左潇龙 - 博客园
- 在线进制转换
- 计算机组成原理 第 2 版 P-229