三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

C语言系列之——(4)原码、反码、补码

C语言系列之——(4)原码、反码、补码

原码,反码,补码

  • 1.二进制和进制转换
    • 1.1 什么是进制
    • 1.2 进制间的转换
      • a.十进制转二进制
      • b.二进制转八进制
      • c.二进制转十六进制
  • 2.原码、反码、补码
    • 2.1 前置知识准备
      • a.这三个码是什么
      • b. 不同整数三码的区别
        • b.1 负数与非负数之间
        • b.2 signed和unsigned之间
          • b.2.1 计算机存储单位说明
          • b.2.2 signed和unsigned之间三码的区别
    • 2.2 整数与三码间的转换
      • a.整数->原码
      • b.原码->反码
      • c.反码->补码
      • d.补码->反码->原码
    • 2.3 整数在内存中的存储与补码运算
      • a. 先上结论
      • b. 为什么一定要用补码存储整数
      • c. 补码运算

1.二进制和进制转换

1.1 什么是进制

我们常常听到二进制八进制十进制十六进制这样的说法,这些是什么意思呢
其实,它们只是数值的不同表现形式

每种进制下,从右往左分别是个位,十位,百位……只是不同进制下每一位的权重是不一样的

不同进制下从右往左每一位的权重如下
二进制:2 0 2^0202 1 2^1212 2 2^222……
八进制:8 0 8^0808 1 8^1818 2 8^282……
十进制:10 0 10^010010 1 10^110110 2 10^2102……
十六进制:16 0 16^016016 1 16^116116 2 16^2162……

每一位上的数字在等于其进制数时会往下一位进一位,自己这一位变为0
(如10进制下每一位上的数最大只能是9,当某一位上数变为10时,会向其左边的第一位进1,自己变成0,所以9+1=10,79+1=80,78+30=108)
(十六进制下为表示每一位上大于9的数,用字母A代表10,B表示11,以次类推,一直到F表示15)

对应进制下表示的数字等于其每一位上的数字和对应权重之积的和

**以十五这个数在不同进制下的表示为例:
二进制:1111(15 = 1x2 0 2^020+ 1x2 1 2^121+1x2 2 2^222+1x2 3 2^323= 1 + 2 + 4 + 8 )
八进制:17
十进制:15
十六进制:F


1.2 进制间的转换

a.十进制转二进制

十进制转二进制最简方法为:短除法,其规则可以总结为8个字:除 2 取余,逆序排列

以8转二进制为例:
8 ÷ 2 = 4(余0)
4 ÷ 2 = 2(余0)
2 ÷ 2 = 1(余0)
1 ÷ 2 = 0(余1)
最后余数序列0001逆序排列为:1000

b.二进制转八进制

因为八进制下每一位的数字范围为0~7,刚好可以被三位二进制数覆盖(000 ~ 111),所以二进制转八进制的规则是:从二进制序列的右边低位开始,向左每3个二进制位按从右往左2 0 2^0202 1 2^1212 2 2^222的权重换算成一个八进制位,不足3位的直接换算

以1011转八进制为例:
1011从右往左三个三个可分为两段:011,1
从右往左依次换算成八进制位下数,011 -> 3,1 -> 1
得到的八进制数:13

c.二进制转十六进制

和转八进制类似,从二进制序列的右边低位开始,向左每4个二进制位按从右往左2 0 2^0202 1 2^1212 2 2^2222 3 2^323的权重换算成一个十六进制位,不足4位的直接换算

(其他进制下每位数权重再各位求和即得十进制数)


2.原码、反码、补码

2.1 前置知识准备

a.这三个码是什么

整型数据 / 整数的二进制表示方法有三种:原码反码补码


(char类型变量中存的实际是字符对应地ASCLL码值,所以也是整型类型)

b. 不同整数三码的区别

b.1 负数与非负数之间

总结下来就是:
非负整数的原、反、补码三码相同,只要得到其中一个码就得到了所有三码;负整数的三码各不相同,需要分别计算转换

b.2 signed和unsigned之间
b.2.1 计算机存储单位说明

在计算机中,最小的存储单位是比特(bit)1个二进制位=1bit
接下来比比特大的单位为字节(Byte)1字节=8bit
再之后的存储单位关系如下:

1KB = 1024Byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB

(这里对存储单位进行说明主要是想补充:一个二进制位=1bit 这个前置知识)

b.2.2 signed和unsigned之间三码的区别

signed
1.有符号整数二进制的这三种表示方法下,二进制序列都有符号位数值位之分
2.最高位的1位(即所有比特位中最左边那位)叫做符号位,其余位叫做数值位

3.有符号整数的正负数之分体现在符号位上,而数字的绝对值大小体现在数值位上
4.符号位上为0表示“正”(当然0是不是正数也不是负数),为1表示“负”

(下面以int为例,因为int的大小为4字节,所以其二进制表示下可以分配到32个比特位)

unsigned
二进制序列没有符号位与数值位之分,全是数值位

正是因为符号位和数值位的差别:
同类型的unsigned整数能够表示的最大值为signed整数可表示的最大值的两倍+1
unsigned只可表示非负数,signed可表示负数,0,正数


2.2 整数与三码间的转换

(因为非负整数三码相同,所以下面的原码->反码->补码只针对负整数而言)

a.整数->原码

直接将整数按照正负数的形式翻译成二进制得到的就是原码

inta=-10;-15的原码为:(为方便观看,下面把32个比特位以一个字节为单位每8个间隔开)10000000000000000000000000001010

b.原码->反码

原码符号位不变(如果有的话),其他位依次按位取反就可以得到反码

(按位取反规则:二进制每一位,0变1,1变0)

inta=-10;-10的原码为:(为方便观看,下面把32个比特位以一个字节为单位每8个间隔开)10000000000000000000000000001010-10的反码为:11111111111111111111111111110101

c.反码->补码

反码+1就得到补码(每一位逢2进1变0,若进位后总位数多出正确位数,则舍去多出的位数)

inta=-10;-10的原码为:(为方便观看,下面把32个比特位以一个字节为单位每8个间隔开)10000000000000000000000000001010-10的反码为:11111111111111111111111111110101-10的补码为:11111111111111111111111111110110若反码为:11111111111111111111111111111111其转补码+1100000000000000000000000000000000,比正确的32位多1位,则舍去多出来的那一位1,得补码:00000000000000000000000000000000

d.补码->反码->原码

将上诉的三种转换倒过来执行即可


2.3 整数在内存中的存储与补码运算

a. 先上结论

计算机只认识二进制0和1,而整数实际是以补码的形式存储在内存中的,所以,所有的整数运算都是用补码进行计算的
原码和反码只是人们用来计算补码的工具,内存中并不存在


b. 为什么一定要用补码存储整数

这是因为不用补码有两个硬伤:

1.原码和反码表示下,0都有两种表示形式

-0+0都是同一个数0但是:(原码)-0->10000000000000000000000000000000+0->00000000000000000000000000000000(反码)-0->11111111111111111111111111111111+0->00000000000000000000000000000000这两种表现方式下,同一个数0都占了两个不同的编码,使得可表示的数少了一个,空间直接浪费了一个 而且还会导致运算逻辑混乱 而补码则能完美解决这个问题:+0=-0->00000000000000000000000000000000

2.若不用补码存储整数,减法运算无法直接用加法实现,硬件成本高

计算机的 CPU 里,天生只有加法器,没有专门的减法器。如果用原码做减法,比如 5 - 3,就需要额外的电路来处理符号位、比较绝对值大小,再做减法,这会让硬件变得非常复杂,成本飙升。

而用补码存储整数进行减法运算,可以自动将减法运算变为加法运算,且结果正确

5-3为例 补码下,5-3->5的补码+(-3的补码)5的补码:00000000000000000000000000000101-3的补码:111111111111111111111111111111015+(-3)100000000000000000000000000000010->舍去多的一位->0000000000000000000000000000001000000000000000000000000000000010恰好就是2的补码,5-3=2

c. 补码运算

上面已经说过,所有的整数运算都是用补码进行计算的

那有哪些整数运算呢?

算数运算
常用的算术运算有 + , - , * , / , %
(因为除±外其他都涉及一些位运算规则,所以这里先不进行讲解,等到讲操作符时就会将其他的这些运算补齐)

(在上面讲用补码存储的第二个原因的例子中已体现±运算,所以不再赘述)

注意:一些不同类型的整型变量在进行算数运算时,会发生整型提升算术转换,此时运算的过程就更复杂些,上面举的例子是同类型整型变量间的运算规则,至于整型提升与算术转换的场景,将在后续文章中解释~

(下面这些运算同样会在讲解操作符时补齐)
位运算:~,&,|,^,<<,>>
复合赋值运算:+=,-=,……
比较运算±号

← 返回列表