深入理解计算机系统chapter2.4------整数的表示(无符号编码和补码编码)

目录

 

1、什么是整数?

2、C 语言中的整型数据类型

3、无符号数的编码

4、补码编码 

 5、反码和原码

6、有符号和无符号数之间的转换


1、什么是整数?

  整数包含正整数,0,负整数。我们从小的数学常识,整数是无穷无尽的,即整数的大小没有限制。

  但是在计算机中则不能这样理解,因为计算机是靠数字信号来表示数,计算机所能处理的整数的长度是由计算机的字长来决定的,所以,在计算机中,我们必须制定一个规则来表示整数。

 

 

2、C 语言中的整型数据类型

  C 语言是支持多种整型数据类型的,下面我们看一下在 32 位机器和 64 位机器中,C 语言整型数据类型的取值范围。

  深入理解计算机系统chapter2.4------整数的表示(无符号编码和补码编码)

 

   深入理解计算机系统chapter2.4------整数的表示(无符号编码和补码编码)

 

   我们可以看到 :

  ①、C 语言数据类型是可以用来指定大小,同时还可以指示表示的数是非负数(声明为 unsigned),或者负数(默认)。

  ②、数据类型分配的字节数会根据机器的字长和编译器有所不同,不同的大小所表示的范围是不同的。上图唯一一个与机器有关的取值范围是 long 类型的,64位机器使用8个字节(264),而32位机器使用4个字节(232)。

  ③、负数的范围要比正数的范围大1。这是为什么呢,请接着往下面看。

 

  下面我们看一下 C 语言标准所定义的每种数据类型所能表示的最小的取值范围。

  深入理解计算机系统chapter2.4------整数的表示(无符号编码和补码编码)

  C 语言标准我们可以从上图得到:

  ①、正数和负数的取值范围是对称的。

  ②、int 数据类型可以用 2 个字节来实现。(216)

  ③、long 数据类型用4 个字节来实现。(232

 

 

3、无符号数的编码

     无符号数,在C语言中,即用 unsigned 声明的整数。

  定义:假设对于一个w位的无符号整数,用二进制比特位可以表示为[xw-1 , xw-2 , … , x2 , x1 , x0]。那么我们可以用一个函数表示如下:

  深入理解计算机系统chapter2.4------整数的表示(无符号编码和补码编码)

  这个函数可以举几个简单的例子来看:

  深入理解计算机系统chapter2.4------整数的表示(无符号编码和补码编码)

  那么很显然,对于一个无符号编码的数,由 w 位的二进制序列构成,那么它的最小值,即所有位都为 0 ,用位向量表示即:【000......000】。

    UMinw = 0

  最大值即所有位都为 1,用位向量表示即:【111......111】

     UMaxw = 1 * (1-2w) / 1 - 2 = 2w - 1 

 

   我们可以得出一个结论:无符号的二进制,对于任意一个w位的二进制序列,都存在唯一一个整数介于0 到 2w-1之间,与这个二进制序列对应。反过来,在0 到 2w-1之间的每一个整数,存在唯一的二进制序列与其对应。

 

 

4、补码编码 

  上面我们讲解了正整数的编码,那么在实际应用中,是存在负数的。而在计算机中,最常见的表示有符号的数就是补码。补码的定义如下:

  深入理解计算机系统chapter2.4------整数的表示(无符号编码和补码编码)

  其中最高有效位 xw-1 也称为符号位,符号位为 1 时表示负数,当设置为 0 时,表示非负数。下面我们看几个例子:

  深入理解计算机系统chapter2.4------整数的表示(无符号编码和补码编码)

 

   那么我们可以得出:当最高位为1,其余为全部是 0 的时候,即【1000......000】,表示补码格式的最小值:

    TMinw = -2w-1 

 当最高位为 0,其余为全部是 1 时,即【0111......111】,表示补码格式的最大值:

    TMaxw = 1 * (1 - 2w-1) / 1 - 2 = 2w-1-1

   通过上面的两个公式,我们就很好理解为什么上面C语言数据类型负数的范围要比正数的范围大1。

  和上面无符号编码一样,我们对于补码格式编码也可以得到一个结论:

  对于任意一个w位的二进制序列,都存在唯一一个介于-2w-1 到 2w-1-1的整数,与这个二进制序列对应。反过来,对于任意介于-2w-1 到 2w-1-1的整数,存在唯一的长度为w二进制序列与其对应。

   那么你就应该明白了为什么十进制 -1,在计算机中二进制表示为 1111 1111,而不是1000 0001,因为计算机是以补码的形式表示的。

 

 5、反码和原码

  反码定义:除了最高有效位的权是-2w-1-1,而不是-2w-1其余的和补码表示方式一样

    深入理解计算机系统chapter2.4------整数的表示(无符号编码和补码编码)

  原码定义:最高有效位是符号位,用来确定剩下的位是正还是负

   深入理解计算机系统chapter2.4------整数的表示(无符号编码和补码编码)

  我们可以和补码的定义进行对比:

    深入理解计算机系统chapter2.4------整数的表示(无符号编码和补码编码)

  原码:一个整数,按照绝对值大小转换为二进制数,最高位为符号位。

  反码:将原码除最高位(符号位)外,其余各位按位取反,所得到的二进制码。正数的反码为原码。

  补码:反码最低位加1即为补码。

  对于正整数,原码、反码、补码完全一样,即符号位固定为0,数值位相同。

  对于负整数,原码和补码互相转换的简便方法:从数的右边往左开始数,遇到“0”不理它,直到遇到第一个“1”为止,以后的每一位数取反即是它的原码或补码,符号位不变,还是“1”(补码的补码是原码)。

  比如:11010100 ----- 从右往左数,第一位是0,不理它,第二位还是0不理它,第三位是1,那么从此以后的每位取反,即为它的补码了.答案为:10101100

  事实上,程序员如果希望代码具有最大的可移植性,能够在所有可能的机器上运行,就应该用补码的形式来表示有符号整数。虽然过去生产过基于反码表示的机器,但是几乎所有的现代机器都是使用补码。

  注意:浮点数有使用原码编码。

  关于整型数据类型的表示和取值范围,Java标准是非常明确的,它要求采用补码形式,取值范围和C语言在64位机器中的情况一样。在Java中,单字节数据类型称为 byte,而不是char,而且没有long long 数据类型。这些具体的要求都是为了保证无论在什么机器上,Java程序运行的表现都能完全一样。

 

 

6、有符号和无符号数之间的转换

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5.     char t = 0xFF;
  6.     unsigned char u = (unsigned char)t;
  7.     //%d把对应的整数按有符号十进制输出,%u把对应的整数按无符号十进制输出
  8.     printf("t=%d,t2u=%u\n",t,u);
  9.     return 0;//c标准规定建议main函数返回值为int
  10. }
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  1. #include <stdio.h>
  2.  
  3. int main()
  4. {
  5.     unsigned char u = 0xFF;
  6.     char t = (char)u;
  7.     //%d把对应的整数按有符号十进制输出,%u把对应的整数按无符号十进制输出
  8.     printf("u=%u,u2t=%d\n",u,t);
  9.     return 0;//c标准规定建议main函数返回值为int
  10. }