光天化日学C语言(23)- 整数的存储 | 补码到底有什么用?

Posted 英雄哪里出来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了光天化日学C语言(23)- 整数的存储 | 补码到底有什么用?相关的知识,希望对你有一定的参考价值。

🙉饭不食,水不饮,题必须刷🙉

还不会C语言,和我一起打卡!
🌞《光天化日学C语言》🌞

LeetCode 太难?上简单题!
🧡《C语言入门100例》🧡

LeetCode 太简单?大神盘他!
🌌《夜深人静写算法》🌌

一、前言

  本文作者是从 2007 年开始学 C语言 的,不久又接触了C++,基本就是 C/C++ 技术栈写了 14 年的样子,不算精通,但也算差强人意。著有《夜深人静写算法》系列,且承诺会持续更新,直到所有算法都学完。主要专攻 高中 OI 、大学 ACM、 职场 LeetCode 的全领域算法。由于文章中采用 C/C++ 的语法,于是就有不少读者朋友反馈语言层面就被劝退了,更何况是算法。
  于是,2021 年 06 月 12 日,《光天化日学C语言》 应运而生。这个系列文章主要服务于高中生、大学生以及职场上想入坑C语言的志同道合之人,希望能给祖国引入更多编程方面的人才,并且让自己的青春不留遗憾!
  这一章的主要内容是整数的存储。

二、人物简介

  • 第一位登场的就是今后会一直教我们C语言的老师 —— 光天。
  • 第二位登场的则是今后会和大家一起学习C语言的没什么资质的小白程序猿 —— 化日。

三、整数简介

1、符号位 和 数值位

  • 我们知道 整数 分为 有符号整型 和 无符号整型。
  • 有符号整型,程序需要区分 符号位数值位
  • 对我们人类来说,很容易分辨;而对计算机而言,就要设计专门的电路,这就增加了硬件的复杂性,从而增加了计算的时间。

  所以,如果能够将 符号位数值位 联合起来,让它们共同参与运算,不再加以区分,这样硬件电路就会变得更加简单。

2、整型的加减运算

  • 其次,加法减法 的引入,也将问题变得复杂。而由于减去一个数相当于加上这个数的相反数,例如:1 - 2等价于 1 + (-2)1 - (-2)等价于1 + 2

  所以,它们可以合并为一种运算,即只保留加法运算。

  • 相反数是指 数值位 相同,符号位 不同的两个数,例如,1 和 -1 就是一对相反数。

  • 所以,我们需要做的就是设计一种简单的、不用区分符号位和数值位的加法电路,就能同时实现加法和减法运算。首先让我们看几个计算机中的概念。

四、机器数和真值

1、机器数

  • 我们知道计算机是内部由 0 和 1 组成的编码,无论是整数还是浮点数,都会涉及到负数,对于机器来说是不知道正负的,而 “正” 和 “负” 正好是两种对立的状态,所以规定用 “0” 表示 “正”,“1” 表示 “负”,这样符号就被数字化了,并且将它放在有效数字的前面,就成了有符号数;
  • 把符号 “数字化” 的数称为 机器数;

2、真值

  • 而带有 “+” 或者 “-” 的数称为 真值;
  • 然而,当符号位和数值部分放在一起后,如何让它一起参与运算呢?那就要涉及到接下来要讲的计算机的各种编码了。

五、计算机编码

1、原码

1)定义

  • 这里的原码并不是源码(源代码)的意思,而是机器数中最简单的一种表示形式;为了快速理解,这里只介绍 32位整数;

【定义】 符号位0 代表 正数符号位1 代表 负数数值位真值的绝对值

2)举例

  • 1)对于十进制数 37,它的 真值 和 原码 关系如下:
真值:+ 00000000 00000000 00000000 00100101
原码:  00000000 00000000 00000000 00100101
  • 2)对于十进制数 -37,它的 真值 和 原码 的关系如下:
真值:- 00000000 00000000 00000000 00100101
原码:  10000000 00000000 00000000 00100101
  • 我们发现,对于负数的情况,原码 加上 真值(注意,这里真值为负数)后,二进制数正好等于 1 ( 0...0 ⏟ 31 ) 2 1(\\underbrace{0...0}_{31})_2 1(31 0...0)2, 即 2 31 2^{31} 231,表示成公式如下: [ x ] 原 + x = 2 31 [x]_原 + x = 2^{31} [x]+x=231

3)公式

  • 因此,我们可以通过移项,得出原码的十进制计算公式如下:

[ x ] 原 = { x ( 0 ≤ x < 2 n − 1 ) 2 n − 1 − x ( − 2 n − 1 < x ≤ 0 ) [x]_原 = \\begin{cases} x & (0 \\le x < 2^{n-1})\\\\ 2^{n-1} - x & (-2^{n-1} < x \\le 0) \\end{cases} [x]={x2n1x(0x<2n1)(2n1<x0)   这里 x x x 代表真值,而 n n n 的取值是 8 、 16 、 32 、 64 8、16、32、64 8163264,我们通常说的整型int都是 32位 的,本文就以 n = 32 n = 32 n=32 的情况进行阐述;

  • 原码是最贴近人类的编码方式,并且很容易和真值进行转换,但是让计算机用原码进行加减运算过于繁琐,如果两个数符号位不同,需要先判断绝对值大小,然后用绝对值大的减去绝对值小的,并且符号以绝对值大的数为准,本来是加法却需要用减法来实现。

2、反码

1)定义

【定义】 正数反码 就是它的 原码负数反码原码 的每一位的 0变11变0(即位运算中的按位取反);

2)举例

  • 1)对于十进制数 37,它的 真值 和 反码 关系如下:
真值:+ 00000000 00000000 00000000 00100101
反码:  00000000 00000000 00000000 00100101
  • 2)对于十进制数 -37,它的 真值 和 反码 的关系如下:
真值:- 00000000 00000000 00000000 00100101
反码:  11111111 11111111 11111111 11011010
  • 我们发现,对于负数的情况,反码 减去 真值(注意,这里真值为负数)后,负负得正,转换成二进制位相加正好等于 ( 1...1 ⏟ 32 ) 2 (\\underbrace{1...1}_{32})_2 (32 1...1)2, 即 2 32 − 1 2^{32}-1 2321,表示成公式如下: [ x ] 反 − x = 2 32 − 1 [x]_反 - x = 2^{32}-1 [x]x=2321

3)公式

  • 因此,通过移项,我们可以得出反码的十进制计算公式如下:

[ x ] 反 = { x ( 0 ≤ x < 2 n − 1 ) 2 n − 1 + x ( − 2 n − 1 < x ≤ 0 ) [x]_反 = \\begin{cases} x & (0 \\le x < 2^{n-1})\\\\ 2^{n}-1 + x & (-2^{n-1} < x \\le 0) \\end{cases} [x]={x2n1+x(0x<2n1)(2n1<x0)   这里 x x x 代表真值,而 n n n 的取值是 8 、 16 、 32 、 64 8、16、32、64 8163264,我们通常说的整型int都是 32位 的,本文就以 n = 32 n = 32 n=32 的情况进行阐述;

  • 反码有个很难受的点,就是 ( 0 0...0 ⏟ 31 ) 2 (0\\underbrace{0...0}_{31})_2 (031 0...0)2 ( 1 0...0 ⏟ 31 ) 2 (1\\underbrace{0...0}_{31})_2 (131 0...0)2 都代表零,就是我们常说的 正零 和 负零。正如公式中看到的,当真值为 0 的时候,有两种情况,这就产生了二义性,而且浪费了一个整数表示形式。

3、补码

1)定义

【定义】 正数补码 就是它的 原码负数补码 为 它的反码加一

2)举例

  • 1)对于十进制数 37,它的 真值 和 补码 关系如下:
真值:+ 00000000 00000000 00000000 00100101
补码:  00000000 00000000 00000000 00100101
  • 2)对于十进制数 -37,它的 真值 和 反码 的关系如下:
真值:- 00000000 00000000 00000000 00100101
补码:  11111111 11111111 11111111 11011011
  • 我们发现,对于负数的情况,反码 减去 真值(注意,这里真值为负数)后,负负得正,转换成二进制位相加正好等于 1 ( 0...0 ⏟ 32 ) 2 1(\\underbrace{0...0}_{32})_2 1(32 0...0)2, 即 2 32 2^{32} 232,表示成公式如下: [ x ] 补 − x = 2 32 [x]_补 - x = 2^{32} [x]x=232

3)公式

  • 因此,通过移项,我们可以得出补码的十进制计算公式如下:

☀️光天化日学C语言☀️(34)- 函数进阶 | 面向过程编程

☀️光天化日学C语言☀️(33)- 函数入门 | 开启下一个篇章!

光天化日学C语言(21)- 逗号运算符 | 压缩代码的神器

☀️光天化日学C语言☀️(32)- continue 关键字 | 下一个!

☀️光天化日学C语言☀️(36)- 作用域

☀️光天化日学C语言☀️(29)- while 语句 | 死循环啦!