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

英雄哪里出来 发表于 2021/08/17 07:06:38 2021/08/17
【摘要】 补码到底有什么用?

一、前言

  本文作者是从 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 , 即 2 31 2^{31} ,表示成公式如下:$$ [x]_原 + x = 2^{31}$$

3)公式

  • 因此,我们可以通过移项,得出原码的十进制计算公式如下:
[x]_原 = \begin{cases} x & (0 \le x < 2^{n-1})\\ 2^{n-1} - x & (-2^{n-1} < x \le 0) \end{cases} $$   这里 $x$ 代表真值,而 $n$ 的取值是 $8、16、32、64$,我们通常说的整型```int```都是 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 , 即 2 32 1 2^{32}-1 ,表示成公式如下:$$ [x]_反 - x = 2^{32}-1$$

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}

  • 反码有个很难受的点,就是 ( 0 0...0 31 ) 2 (0\underbrace{0...0}_{31})_2 ( 1 0...0 31 ) 2 (1\underbrace{0...0}_{31})_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 , 即 2 32 2^{32} ,表示成公式如下:$$ [x]_补 - x = 2^{32}$$

3)公式

  • 因此,通过移项,我们可以得出补码的十进制计算公式如下:
[x]_补 = \begin{cases} x & (0 \le x < 2^{n-1})\\ 2^{n} + x & (-2^{n-1} \le x < 0) \end{cases} $$   这里 $x$ 代表真值,而 $n$ 的取值是 $8、16、32、64$,我们通常说的整型```int```都是 32位 的,本文就以 $n = 32$ 的情况进行阐述;

对于三种编码方式,总结如下:
  1)这三种机器数的最高位均为符号位;
  2)当真值为正数时,原码、反码、补码的表示形式相同,符号位用 “0” 表示,数值部分真值相同;
  3)当真值为负数时,原码、反码、补码的表示形式不同,但是符号位都用 “1” 表示,数值部分:反码是原码的 “按位取反”,补码是反码加一;

正数

真值:+ 00000000 00000000 00000000 00100101
原码:  00000000 00000000 00000000 00100101
反码:  00000000 00000000 00000000 00100101
补码:  00000000 00000000 00000000 00100101

负数

真值:- 00000000 00000000 00000000 00100101
原码:  10000000 00000000 00000000 00100101
反码:  11111111 11111111 11111111 11011010
补码:  11111111 11111111 11111111 11011011

六、为什么要引入补码

  • 最后,我们来讲一下引入补码的真实意图是什么。

1、主要目的

  • 计算机的四则运算希望设计的尽量简单。但是引入 符号位 的概念,对于计算机来说还要考虑正负数相加,等于引入了减法,所以希望是计算机底层 只设计一个加法器,就能把加法和减法都做了。

2、原码运算

  • 对于原码的加法,两个正数相加的情况如下:
+1 的原码:00000000 00000000 00000000 00000001
+1 的原码:00000000 00000000 00000000 00000001
----------------------------------------------
+2 的原码:00000000 00000000 00000000 00000010
  • 好像没有什么问题?于是人们开始探索减法,但是起初设计的人的初衷是希望不用减法,只用加法运算就能够将加法和减法都包含进来,于是,我们尝试用原码的负数表示来做运算;
  • 1 - 2表示成1 + (-2),然后用原码相加得到:
+1 的原码:00000000 00000000 00000000 00000001
-2 的原码:10000000 00000000 00000000 00000010
----------------------------------------------
-3 的原码:10000000 00000000 00000000 00000011
  • 我们发现1 + (-2) = -3,计算结果明显是错的,所以为了解决减法问题,引入了反码;

3、反码运算

  • 对于正数的加法,两个正数反码相加的情况和原码相加一致,不会有问题。
  • 对于正数的减法,转换成一正一负两数相加。
  • 1 - 2表示成1 + (-2),情况如下:
+1 的反码:00000000 00000000 00000000 00000001
-2 的反码:11111111 11111111 11111111 11111101
---------------------------------------------- 
-1 的反码:11111111 11111111 11111111 11111110
  • 没有什么问题?但是某种情况下,反码会有歧义,当两个相同的数相减时,即1 - 1表示成1 + (-1),情况 如下:
+1 的反码:00000000 00000000 00000000 00000001
-1 的反码:11111111 11111111 11111111 11111110
---------------------------------------------
-0 的反码:11111111 11111111 11111111 11111111
  • 这里出现了一个奇怪的概念,就是 “负零”,反码运算过程中会出现有两个编码表示零这个数值。
  • 为了解决正负零的问题引入了补码的概念。

4、补码运算

1)两个正数的补码相加。

  • 其和等于 它们的原码相加,已经验证过,不会有问题;

2)一正一负两个数相加,且 答案非零

+1 的补码:00000000 00000000 00000000 00000001
-2 的补码:11111111 11111111 11111111 11111110
---------------------------------------------- 
-1 的补码:11111111 11111111 11111111 11111111
  • 结果正确;

3)一正一负两个数相加,且 答案为零

+1 的补码   00000000 00000000 00000000 00000001
-1 的补码: 11111111 11111111 11111111 11111111
---------------------------------------------- 
0 的补码:1 00000000 00000000 00000000 00000000
  • 两个互为相反数的数相加后,得到的数的补码为 2 n 2^n (可以认为是是溢出了),所以那个 1 根本不会被存进计算机中,也就是表现出来的结果就是 零!
  • 而且,补码的这个运算,和我们之前提到的定义吻合。
  • 综上所述,补码解决了整数加法带来的所有问题。

通过这一章,我们学会了:
  1)原码的表示形式;
  2)反码的表示形式;
  3)补码的表示形式;

  • 希望对你有帮助哦 ~ 祝大家早日成为 C 语言大神!

课后习题


📢博客主页:https://blog.csdn.net/WhereIsHeroFrom
📢欢迎各位 👍点赞 ⭐收藏 📝评论,如有错误请留言指正,非常感谢!
📢本文由 英雄哪里出来 原创,转载请注明出处,首发于 🙉 CSDN 🙉
作者的专栏:
  👉C语言基础专栏《光天化日学C语言》
  👉C语言基础配套试题详解《C语言入门100例》
  👉算法进阶专栏《夜深人静写算法》

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:cloudbbs@huaweicloud.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。