文章分为两部分:一个是整数,一个是小数

先说整数

假设我们有1个字节来存储数字

一个字节 = 8个比特位,按理来说能表示的数字范围为:0(00000000)~ 256(11111111)

但是如果有负数的话怎么处理?

方法是:将最高位表示成符号位,其中0代表正数,1代表负数。

即:1 = 00000001,-1 = 10000001

仅仅这样的话,运算(比如1+ -1)对机器就变得非常不方便。为了运算方便,我们引入了原码,反码,补码的概念。

原码:最初的二进制位,其中最高位为符号位

反码

  • 如果是正数,原码 = 反码;
  • 如果是负数, 将0变为1,1变为0,符号位固定(这里的符号固定意味不能将符号位的1变为0

补码

  • 如果是正数,原码 = 反码 = 补码 ;
  • 如果是负数, 对反码进行加 1 操作,符号位固定(这里的符号位固定意味着从反码变为补码时,符号位不参与进位操作,从补码变为反码时不参与借位操作

这样的话 1 和 -1 就如下表:

数字 1 -1
原码 00000001 10000001
反码 00000001 11111110
补码(计算机实际存储的格式) 00000001 11111111

此时 1+(-1) = 00000000 (二进制)= 0(十进制)

加法运算时,符号位是参与运算的

[Tip]
写到这里我们来解释一下为什么两数相减( 或者说正数和负数相加),最后的结果等于两数字补码相加的结果。
比如有个钟表,现在指向的是10点,我们想让他指向5点。有两个办法,一个是顺时针拨动7点,一个是逆时针拨动5点,即:

  • 10 + 7 = 17 -12 = 5
  • 10 - 5 = 5

在这个例子中,-5 的补码就是 7,所以减法运算的公式就为:

  • X - Y = X + (- Y)的补码

当按照补码格式来存储数字时,一个字节的存储范围就是:

补码 十进制
00000000 0
00000001 1
00000010 2
…… ……
01111111 127(因为最高位为符号位,所以127就是一个字节能存储的正数的上限)
10000000 -0(对01111111进行+1操作变为10000000,但是因为是补码,所以10000000 = 0)
10000001 -127
10000010 -126
…… ……
11111110 -2
11111111 -1

补码的分布看起来像个圆

上面说到,对于一个字节来说:

  • 如果是无符号的话能表示的数字范围为 0(00000000)~ 256(11111111)
  • 当有了符号,能表示的数字范围就为:-127(11111111)~ 127(01111111)
  • 但是实际上对于一个字节来说能表示的数字范围其实是 -128 ~ 127,127能理解,-128是为什么?
  • 按照上面的表格来说,范围依然是 -127 ~ 127 ,怎么来的 -128 呢?其实是因为在补码中由于 00000000 表示的是0 ,10000000 表示的也是 0 ,所以就人为规定 10000000(补码) = -128
  • 正是由于计算机是按补码存储的,所以范围才是 -128 ~ 127。

但是要注意 -128 是没有对应的反码和原码的(在一个字节里)。

我们总结一下,在一个字节中:

  • 反码和原码的表示范围是:-127 ~ 127,
  • 补码: -128 ~ 127,
  • 由于计算机是按补码存储的,所以范围是: -128 ~ 127

这就是整数在电脑中的存储,那么小数是在电脑中如何存储的呢?

小数存储

在C中我们一般用到小数,选择的数据类型是 float 或者 double,所以我们就以 float 来举例说明。

已知 float 在64位机器占4个字节 = 32个比特位.

假设有一个小数 = 5.5,我们要把它存储进32个比特位里面,但首先我们要确定下面几点:

  • 5.5(十进制) = 101 . 1(二进制)
  • 注意不是 101.101,因为 0.5 是 1 乘以 (2 ^ -1)
  • 即:101.1 是 1 * (2^2) + 0 * (2^1) + 1 * (2^0) + 1 * (2^-1)
  • 所以 101.1 等于 101.1 * (2^0) ,表示成科学计数法为 1.011 * (2^2)

将 5.5 = 1.011 * 2^2 存储进32个比特位:

位数 内容
[1]第一位 存的是正负号(这里是0)
[8]接下来八位 存的是指数上的数据(这里是2)
[23]23个比特位 存的是小数点后面的数字,(这里是0110000000000000……)

而对于 double 类型来说:

位数 内容
[1]第一位 存的是正负号
[11]接下来十一位 存的是指数上的数据
[52]52个比特位 存的是小数点后面的数字