Skip to content

1. 浮点数

1.1 为什么负数要用补码表示

对于 int 类型的数字 1 的二进制数表示如下:

补码就是把正数的二进制全部取反再加 1:

假设不用补码的方式来表示负数,而只是把最高位的符号标志位变为 1 表示负数:

可以看到,如果负数不是使用补码的方式表示,则在做基本对加减法运算的时候,还需要多一步操作来判断是否为负数,如果为负数,还得把加法反转成减法,或者把减法反转成加法。

加减法运算在计算机里是很常使用的,所以为了性能考虑,应该要尽量简化这个运算过程。

而用了补码的表示方式,对于负数的加减法操作,实际上是和正数加减法操作一样的。

1.2 十进制小数转二进制

整数部分使用除 2 取余法,小数部分使用乘 2 取整法

但是,并不是所有小数都可以用二进制表示,前面提到的 0.625 小数是一个特例,刚好通过乘 2 取整法的方式完整的转换成二进制。

由于计算机的资源是有限的,所以是没办法用二进制精确的表示 0.1,只能用「近似值」来表示,就是在有限的精度情况下,最大化接近 0.1 的二进制数,于是就会造成精度缺失的情况。

1.3 二进制小数转十进制

小数点前面的指数幂是正数,小数点后面的指数幂是负数

1.4 计算机是怎么存小数的

计算机使用科学记数法来存储小数。

如果二进制要用到科学记数法,同时要规范化,那么不仅要保证基数为 2,还要保证小数点左侧只有 1 位,而且必须为 1。

所以通常将 1000.101 这种二进制数,规格化表示成 1.000101 x 2^3

  • 000101 称为尾数,即小数点后面的数字;
  • 3 称为指数,指定了小数点在数据中的位置;

  • 符号位:表示数字是正数还是负数,为 0 表示正数,为 1 表示负数;
  • 指数位:指定了小数点在数据中的位置,指数可以是负数,也可以是正数,指数位的长度越长则数值的表达范围就越大
  • 尾数位:小数点右侧的数字,也就是小数部分,比如二进制 1.0011 x 2^(-2),尾数部分就是 0011,而且尾数的长度决定了这个数的精度,因此如果要表示精度更高的小数,则就要提高尾数位的长度;

float 中的「指数位」就跟这里移动的位数有关系,把移动的位数再加上「偏移量」,float 的话偏移量是 127,相加后就是指数位的值了,即指数位这 8 位存的是 10000010(十进制 130),因此你可以认为「指数位」相当于指明了小数点在数据中的位置。

在算指数的时候,为什么要加上偏移量?

因为指数是有符号的整数,而有符号整数的计算是比无符号整数麻烦的,所以为了减少不必要的麻烦,在实际存储指数的时候,需要把指数转换成无符号整数。而当我们需要计算实际的十进制数的时候,再把指数减去「偏移量」即可。

float 的指数部分是 8 位,IEEE 标准规定单精度浮点的指数取值范围是 -126 ~ +127,于是为了把指数转换成无符号整数,就要加个偏移量,比如 float 的指数偏移量是 127,这样指数就不会出现负数了。

在从 float 的二进制浮点数转换成十进制时,要考虑到这个隐含的 1,转换公式如下:

二进制只能精准表达 2 除尽的数字 1/2, 1/4, 1/8,但是对于 0.1(1/10) 和 0.2(1/5),在二进制中都无法精准表示时,需要根据精度舍入。