补码、反码,傻傻分不清?

2024-02-03 09:59
文章标签 傻傻 分不清 反码 补码

本文主要是介绍补码、反码,傻傻分不清?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

补码

补码的意义在于表达负数,进而将相对麻烦的减法运算转换为计算机擅长的加法。

我们先来看看,如果单纯用「符号位+原码」的表示方法,会出现什么幺蛾子,比如符号位0表示正、1表示负:

正数二进制原码负数二进制原码
00 0000-01 0000
10 0001-11 0001
20 0010-21 0010
30 0011-31 0011
40 0100-41 0100

当进行两个正数的相加,比如1+2=3:
\begin{matrix} & 0\ 0001 \\ + & 0\ 0010 \\ \hline & 0\ 0011 \end{matrix}

很OK。

两个负数呢?
\begin{matrix} & 1\ 0001 \\ + & 1\ 0010 \\ \hline & 0\ 0011 \end{matrix}

除了符号位由于进位出了差错,绝对值上是没毛病的,只要在运算前单独识别一下正负,不让符号位参与运算就行了。

然而,当正数与负数相加,比如1+(-1),也就是两个正数相减1-1,结果会是我们所期望的0吗?
\begin{matrix} & 0\ 0001 \\ + & 1\ 0001 \\ \hline & 1\ 0010 \end{matrix}

哦不,它不是,它是莫名其妙的-2。

于是就轮到了补码登场。

时钟就是生活中一个用到补码思想的活生生的例子,在我们定时(比如定闹钟)的时候,如果要将原本的6点调整到9点,可以选择朝前拨3个小时,也可以选择朝后拨9个小时。因为在不考虑进位的情况下,6+3=6-9。这意味着,3和-9有着同等的效力,因为在时钟的十二进制中,3就是-9的补码。同理,-8的补码是4,-7的补码是5,等等。规律是,两者的绝对值之和为12。

推广到二进制上,-1的补码是1(两者绝对值之和为2),-01的补码是11(两者绝对值之和为4),-001的补码是111(两者绝对值之和为8)……
总结而言:n位二进制负数a的补码为:
2^n-|a|

这个公式已经清晰明了,如果用十进制来进一步理解,就变得更加简单。比如你买了3块钱的东西,给老板一张10元纸币,老板就需要找你7元。如果你给老板一张100元的纸币,老板就要找你97元。这个找头,就是补码。

于是,对于4位二进制数,有:(正数还是写作原码)

正数二进制补码负数二进制补码
00000-00000
10001-11111
20010-21110
30011-31101
40100-41100

我们不再需要符号位,并且正负数的相加运算就变得完美无瑕,比如1+(-2):
\begin{matrix} & 0001 \\ + & 1110 \\ \hline & 1111 \end{matrix}

原来补码就是这么一个简单的概念,那为什么印象里总觉得十分混乱呢?罪魁祸首就是该死的反码。

反码

反码就是对原码的每一位进行取反,即n位二进制负数a的反码为:
\begin{matrix} \underbrace{ 11\cdots1 } & -\ |a| \\ n个1 \end{matrix}

n位十进制负数a的反码为:
\begin{matrix} \underbrace{ 99\cdots9 } & -\ |a| \\ n个9 \end{matrix}

这个概念其实很没必要,它存在的唯一意义就是方便心算补码。还记得那句宛如绕口令般的「口诀」吗?

补码等于原码的反码加1

这个「加1」是怎么来的,且看:
\begin{cases} a的补码 = 2^n - |a| \\ a的反码 = (2^n-1) - |a| \end{cases}

这就好比你买了3块钱的东西,却非要付99元……

尽管反码能帮助初学者快速心算补码,却也容易给我们的长期记忆造成混乱。我打赌,长久不碰之后,你甚至一时半会想不起来反码和补码的区别。

英文中的补码

其实在英文中,反码也叫补码。

30022-56a4f23321563fa0.jpeg

但两者的名称反而更容易让人理解。比如对于二进制,补码叫做「two's complement」,反码叫做「ones' complement」。注意那一撇的位置,前者可以理解为「二进制的补码」,后者可以理解为「一堆1的补码」,或简称「补1码」。

对于十进制,补码就是「ten's complement」,反码就是「nines' complement」(补9码)。

「complement」就是「补充、补足」的意思,它强调的是「补」,翻译为「补码」再合适不过,「反码」看似讨巧,却徒增了我们的理解负担。

所以忘掉反码吧,就把补码留下。

补码算法溯源

下面以对人友好的十进制为例,介绍两种使用补码将减法转换为加法的方法。比如要算:
\begin{matrix} & 512 \\ - & 128 \\ \hline & 384 \end{matrix}

第一种方法是利用被减数的补9码:

  1. 计算被减数的补9码:999 - 512 = 487
  2. 将被减数的补9码与减数相加:487 + 128 = 615
  3. 计算上一步结果的补9码,即最终结果:999 - 615 = 384

该方法的本质是:
a - b = 999 - [(999 - a) + b]

第二种方法是利用减数的补9码:

  1. 计算减数的补9码:999 - 128 = 871
  2. 将被减数与减数的补9码相加:512 + 871 = 1383
  3. 丢弃上一步结果最高位上的1:1383 - 1000 = 383
  4. 将上一步结果加1,即最终结果:383 + 1 = 384

该方法的本质是:
a - b = a + (999-b) - 1000 + 1

也等价于直接利用减数的补码:
a - b = a + (1000-b) - 1000

作为计算机补码运算的开山鼻祖,帕斯卡的算术机中用的是第一种方法,而如今的计算机用的是第二种方法。

参考资料

  • 维基 - Method of complements
  • 知乎 - 为什么计算机采用补码而不是原码或反码?
  • 简书 - 补码杂谈


2019年11月8日 无锡

这篇关于补码、反码,傻傻分不清?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/673798

相关文章

原码、反码、补码新解

世界上有10中人,一种懂二进制,一种不懂二进制。我们习惯了十进制计数,乍看到二进制,有点别扭,认识后慢慢发现它的神奇:有点一生二,二生万物的意思。十进制和二进制的部分对应关系如下: 小范围的十进制运算,我们操练起来麻麻溜溜的,二进制的运算相信你也不差,然,碰到十进制转二进制的运算就有点蒙圈了。 计算机 CPU 的运算器只实现了加法器,没有实现减法器。但,我们可以通过加上一个负数来实现减法运

代码开源许可证傻傻分不清 Apache MIT GPL的区别

https://www.ruanyifeng.com/blog/2011/05/how_to_choose_free_software_licenses.html

C语言操作符详解1(含进制转换,原反补码)

文章目录 一、操作符的分类二、二进制和进制转换1.二进制与十进制的相互转换2,二进制与八进制的相互转换3.二进制与十六进制的相互转换 三、原码、反码和补码四、移位操作符1.左移操作符(1)左移操作符移位方法(2)左移操作符规律总结 2.右移操作符(1)逻辑右移移位方法(2)逻辑右移规律总结(3)算术右移移位方法(4)算术移位规律总结 五、位操作符:&、|、^、~1.按位与操作符&2.按位或

【软考】——原码、反码、补码、移码

在计算机中,数据编码方式可以有多种,最为常见的有原码、反码、补码、移码。在进行原码,反码,补码,移码之间的转换的时候首先要将他们转换为二进制,在下面的讲解中以17为例来进行讲解。 将17转换为八位数的二进制为00010001。 【原码】      在原码中,将最高位用作符号位(0表示正数,1表示负数),其余各位代表数值本身的绝对值的表示形式,正数的原码是本身,负数的原码只

原码 反码 补码 及 python的位运算

令计算机字长为8位 原码: 正数转化为2进制,负数第一位取1。 1 : 0000 0001 -1 : 1000 0001 反码: 正数的原码不变,负数为原码符号位不变,其余取反。 1 : 0000 0001 -1 : 1111 1110 补码: 正数原码不变,负数为原码符号位不变,其余的按位取反再加一,故1000 0000可以表示为-128 1: 0000 0001 -1 :

原码、反码、补码的由来和计算

引言      在软考教程第一章中有一节是数据表示。这一节介绍了原码、反码和补码,当时只是记住正负数的每个码的转换规则,却没有想一下为什么要用这么多形式的码来表示一个数据,也没有对三种码的转换进行总结,今天让我们来揭开奥秘! 一、机器数和真值: 1、  机器数         各种数值在计算机中表示的形式称为机器数。特点是采用二进制计数制,数的符号用0和1表示,小数点则隐含,表示

补码反码转换

十六进制问题 扫描输入一个字节数组   15  00  29  f9 a1 d1 输出变成  21 0 41 -7 -95 -47      -47————1010 1111(16*2+15)——11010000————11010001 d1 原来是按照十六进制识别识别为10进制的负数 正数的补码不变,负数的补码先取反(符号位不取反)后加1

原码、反码和补码详细集合

目录 一.什么是原码,反码,补码? 1).原码(true form): 2).反码: 3).补码: 二.为什么要有原码,反码,补码  一.什么是原码,反码,补码? 1).原码(true form): 原码是计算机中对数字的一种二进制定点表示方法。 在原码中,取最高位用做符号位(0表示正,1表示负)其余位表示数值大小(该数真值的绝对值)。 整数:原码即其二进制。 小数:

Java之运算符,位运算(源码反码补码)和基本数据类型

文章目录 1 java运算符1.1 各个运算符一览1.2 部分运算符说明1.3 java基本位操作1.3.1 位操作符号1.3.2 原码反码补码1.3.2.1 相关定义1.3.2.2 为何要使用原码, 反码和补码1.3.2.3 负数运算1.3.2.4 转换16进制为什么需要 &0xff 1.3.3 常用的位运算符运算1.3.3.1 左右位移 2 基本数据类型 1 java运算符

Pycharm上python和unittest两种姿势傻傻分不清楚

经常有人在群里反馈,明明代码一样的啊,为什么别人的能出报告,我的出不了报告;为什么别人运行结果跟我的不一样啊。。。 这种问题先检查代码,确定是一样的,那就是运行姿势不对了,一旦导入unittest模块,pycharm会自动识别以unittest的姿势去运行了。   一、unittest运行单个用例 1.如下代码,如果我只想运行其中的一个,如test1,如何运行呢?   2.如果想运行哪