光天化日学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]原={x2n−1−x(0≤x<2n−1)(−2n−1<x≤0) 这里 x x x 代表真值,而 n n n 的取值是 8 、 16 、 32 、 64 8、16、32、64 8、16、32、64,我们通常说的整型
int
都是 32位 的,本文就以 n = 32 n = 32 n=32 的情况进行阐述;
- 原码是最贴近人类的编码方式,并且很容易和真值进行转换,但是让计算机用原码进行加减运算过于繁琐,如果两个数符号位不同,需要先判断绝对值大小,然后用绝对值大的减去绝对值小的,并且符号以绝对值大的数为准,本来是加法却需要用减法来实现。
2、反码
1)定义
【定义】 正数 的 反码 就是它的 原码;负数 的 反码 为 原码 的每一位的 0变1、1变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 232−1,表示成公式如下: [ x ] 反 − x = 2 32 − 1 [x]_反 - x = 2^{32}-1 [x]反−x=232−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} [x]反={x2n−1+x(0≤x<2n−1)(−2n−1<x≤0) 这里 x x x 代表真值,而 n n n 的取值是 8 、 16 、 32 、 64 8、16、32、64 8、16、32、64,我们通常说的整型
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)- 函数入门 | 开启下一个篇章!