近日沉迷ACM不能自拔,恰巧碰到一个位运算的题目卡在了原理上,主要涉及了补码的问题。查了不少资料,看到了一些讲的很好的文章,但细读下来也存在一些问题。

回头翻看教科书,发现多数教科书也并未给出很好的解释。

这一部分内容在高校大一时的大学计算机基础有教授,但限于当时对进位制掌握不完全,编码也没有初步理解,因此多数人还是“满秩茫然”的状态。

这篇文是按照我的思路整理必要的概念和原理,希望对希望“刨根问底”、究其根源的同学有一些帮助。

本文分为两部分:先阐述概念和结论,再探究概念的合理性及结论的正确性。

这篇文章的目的是希望读者在仅掌握进位制知识(具体来说是二进制)的前提下能理解原码、反码、补码的概念和由来。

为了讨论问题方便,通篇均假设:一个十进制数由4位二进制数表示。暂且忽略int类型的值占用4字节的问题。位数问题仅涉及取值范围,不影响讨论的一般性。

还要说一个“人尽皆知”的事实:数据在计算机内存中是以补码的形式存储的。

一. 原码、反码、补码的概念:

原码:数字本身的二进制表示+符号位,其中符号位占用数字本身二进制的最高位。原码的具体位数(bits)由操作系统、数据类型、编译器联合决定。

举个例子:十进制的1即为0001,其中最高位0为符号位,表示正数。如果最高位为1则表示负数。

反码:除去符号位,原码的其他位按位取反。

补码:正数的补码就是原码,而负数的补码为其反码+1。于是我们结合反码的定义,就得到与教科书上一致的结论:负数的补码=(除符号位)原码按位取反再+1。

二. 合理性探究:

我们关心的并不是简单的结论,况且从上面概念来看,整个过程完全是我们人为定义的,定义是否合理是要存疑的(特别是补码定义是否合理)。

接下来我们来看补码是怎么来的,并以此来考证上述三个定义的合理性。

原码约定了一种对正负数的编码方式,对于只有正数或只有负数参与的运算,这种编码方式是没问题的,例如:

1+2=3和-1+(-2)=-3分别为0001+0010=0011和1001+1010=1011,运算是完全正确的。

但如果运算中涉及正负数的混合运算就会出现错误:

1+(-1)=0,如果用原码计算则为0001+1001=1010,显然,原码1010是十进制的-2,这与事实相矛盾。

要解决这个问题必然要做出一些改动,做出的改动要简单可计算且符合我们的习惯。称改动后的编码为补码(至于为什么用补码来称呼后面会解释)。

一个很简单的想法:

① 考虑到希望补码更符合习惯——我们更“喜欢”正数,因此规定正数的补码仍然为它自己本身。

现在我们在①的条件下先考虑一个问题,0的补码是什么?

显然0是没有正负的,但是根据原码的定义,0可表示为0000,也可表示为1000,这无形之中就产生了+0和-0(请与高等数学中的0+和0-区分,这里不是指趋近于0的不同方式)。这里就产生了第二个条件:

② 我们希望0就是0而不分正负,因此认为0的补码就是它本身的二进制表示,至于“-0”我们将它放到负数的最后,也就是取值范围内最小值的绝对值比最大值大1。

(题外话)从这个条件也能理解有符号数的范围问题:

例如int类型变量,占用4字节=32位,取值范围理应为,是关于0对称的,但由于“-0”,取值范围内负值的最小值就多了一个,因此int类型变量的取值范围就变成了

至此,我们完成了0和正数的补码,接下来解决负数的补码。

由于在计算机内部只能处理二进制信息,因此编码的意义在于与计算机约定每种信息在计算机内部对应的二进制表示。同时编码也要与我们人类世界的法则相吻合。对于数的编码,就要与四则运算吻合。回过头来看之前的1+(-1)=0,现在我们知道了0和1的补码,为了让-1的补码符合四则运算,不妨列个方程,先设-1的补码为x,将1+(-1)=0写为补码形式:0001+x=0000,解得x即为-1的补码。显然x=0000-0001。到这一步再往下作计算似乎就有点疑问:二进制较小的数减较大的数如何计算?

这里的减法其实是一个相当于在最高位前添一位1的减法,这么说似乎又有点“钦定”的感觉。由于减法可以从加法定义,所以从加法来解释会更容易理解:

考虑二进制加法1111+0001=?。显然,如果不限制二进制位数的话结果应该为1 0000,但是在计算机中,一个数据类型的位数是固定的,如果算式限制在4位二进制数的话,那么结果只好舍弃最高位进位的1,结果是0000,这就是“进位丢失”。完整的算式为1111+0001=0000,现将0001移项至等号右端,则变为1111=0000-0001,这也就是刚刚所说的在最高位前添一位1的减法。

现在再来看-1的补码问题,如前所说x=0000-0001=1111。对这个算式做个抽象,我们似乎能得到这么一个一般性的结果:

一个负数的补码=0的补码-这个负数的相反数的补码

又因为我们知道0的补码是0000,结合“进位丢失”所述,0000=1111+0001,用这个等式右端代替0的补码即为:

一个负数的补码=(1111+0001)-这个负数的相反数的补码

用交换律调换一下等式右边的顺序:

一个负数的补码=(1111-这个负数的相反数的补码)+0001

来思考上面这个式子右边括号里的东西是什么:“这个负数的相反数的补码”其实就是正数的补码,也就是原码。用1111减去一个正数的原码,不妨随便找个值来看,例如1111-0110=1001,这不就相当于对这个正数包括符号位按位取反么。回头来看,结果就很接近负数补码的结论了:

一个负数的补码=这个负数的相反数的补码包括符号位按位取反+1

“这个负数的相反数的补码包括符号位按位取反”的做法等同于“保留这个负数的原码的符号位不变,其余位按位取反”(例如:对0001全部按位取反结果为1110,只对1001除去最高位的其他位按位取反结果同样为1110),同时规定“除去这个负数的原码的符号位,其余位按位取反”这种做法得到的结果叫做反码,这样就与开篇概念中补码的结论一致——“负数的补码为其反码+1”。

至此,验证了补码的定义是合理的,也算是道清了补码的来龙去脉。

19.9.15 补丁1:

三年后看三年前的自己真的是菜的不行,做个补丁吧。结论搞得太复杂了,反码这东西本身就是中间产物,加上反码的概念之后搞得又乱又难受。简化版的结论就是:负数(-x)的补码=该负数绝对值(x)的补码按位取反+1。

19.9.15 补丁2:

整个推理过程最精髓的部分在于用0000=1111+0001将推导过程中的0替换。

原码、反码、补码以及补码是怎么来的相关推荐

  1. 原码 反码 换算工具 补码_原码和补码的换算(原码反码补码转换工具)

    [-3]反=[10000011]反=11111100 原码 反码 负数的补码是将其原码除符号位之. 两个说法都没有错,我们举个例子来看看就明白了:1.10001的补码是取反后在再加1,也就是11110 ...

  2. 原码 反码 换算工具 补码_原码/反码/补码在线计算器

    原码/反码/补码计算器,在线计算给定整数的原码/反码/补码. 原码, 反码和补码的概念 对于一个数, 计算机要使用一定的编码方式进行存储. 原码, 反码, 补码是机器存储一个具体数字的编码方式. 原码 ...

  3. 原码 反码 换算工具 补码_原码,反码,补码相互转换在线计算器_三贝计算网_23bei.com...

    本计算软件适用于10进制.16进制.2进制数值原码.反码.补码的计算. 输入已知数据变量.选择已知变量的类型(支持原码(10进制).原码(16进制).原码(2进制).反码(2进制).反码(16进制). ...

  4. 由Python位运算到原码反码补码

    采用书籍Python核心编程(第二版),人民邮电出版社,2008年7月第1版.本书以Python2.5为主,但笔记主要以Python3.6为主. 一.Python位运算操作符 Python支持标准位运 ...

  5. 关于计算机中 原码, 反码, 补码 详解

    本篇文章讲解了计算机的原码, 反码和补码. 并且进行了深入探求了为何要使用反码和补码, 以及更进一步的论证了为何可以用反码, 补码的加法计算原码的减法. 论证部分如有不对的地方请各位牛人帮忙指正! 希 ...

  6. 原码, 反码, 补码, 移码 详解

    本篇文章讲解了计算机的原码, 反码和补码. 并且进行了深入探求了为何要使用反码和补码, 以及更进一步的论证了为何可以用反码, 补码的加法计算原码的减法. 论证部分如有不对的地方请各位牛人帮忙指正! 希 ...

  7. 关于 原码 反码 补码 位运算

    二进制 原码:最高位为符号位,0为正  1为负 正数的原码 反码 补码 都是相同的 反码:负数的反码为原码符号位不变 其它对应变化(1变0 0变1) 补码:等于 反码+1 3^-3 =? 运算过程 - ...

  8. 051_原码反码补码概念

    1. 反码的范围 1.1. 反码表示法规定: 正数的反码与其原码相同.负数的反码是对其原码逐位取反, 但符号位除外. 1.2. 在规定中, 8位二进制码能表示的反码范围是-127~127. 1.3. ...

  9. 10.原码 反码 补码

    +7的原码:0000 0111 -7的原码:1000 0111   第一位0代表正数,1代表负数,第一位为符号位 +7的反码:0000 0111 正数反码和原码一样 -7的反码:1111 1000   ...

  10. java进制原码_Java 一一 进制、原码 反码 补码、移位操作

    进制 二进制 和 十进制 相互转换 十进制 和 十六进制 相互转换 原码,反码,补码 原码.反码.补码: 在计算机内, 有符号数有三种表示法: 原码, 反码, 补码. 所有的数据的运算都是采用 补码 ...

最新文章

  1. MyEclipse扩展功能设置(Eclipse代码提示功能)
  2. 基于DM8168 EVM的智能视频跟踪系统
  3. java 基本类型内存_java基本数据类型、内存分析、装包拆包
  4. [上海]LinkCoder第四期活动——Jeffrey Richter:Win 8应用开发与.NET4.5
  5. javaweb关于用户是否登录全局判断,没有登录跳转到登录界面
  6. Windows上配置SSHKey到GItHub
  7. 摄像头夜间拍摄画面有拖影_iQOO 3延续vivo人像拍摄基因 这些技术必须了解
  8. 微信浏览器打开网页被拦截了?Mindjump快速解决微信屏蔽网址用户打不开的难题...
  9. php和web服务器,php与web服务器关系
  10. 【Servlet】解决org.apache.tomcat.util.modeler.BaseModelMBean.invoke Exception invoking method……
  11. bzoj1084 [SCOI2005]最大子矩阵 dp
  12. SmallMQ实现发布
  13. php底层深度探索(3) ---Apache启动阶段分析 王泽宾
  14. 190119每日一句
  15. 又一篇好文:折磨人的商业计划书
  16. 柔性机械臂_机械臂位置控制概述
  17. 【测试】26.用户需求规格跟踪矩阵
  18. 银行票据+票据池相关的项目讲解
  19. 综合布线双绞线的线序标准568A与568B
  20. 阿里云免费服务器搭建个人博客

热门文章

  1. PCB正片与负片之分以及实际使用建议
  2. 供应链金融系统-强大的供应链风控体系为金融平台保驾护航
  3. 使用cwRsync实现windows下文件定时同步
  4. oracle表同步 job,Oracle中通过Job实现定时同步两个数据表之间的数据
  5. ubuntu dkms报错
  6. java自定义异常必须继承什么类_49.Java-自定义异常类
  7. echarts最简单的南丁格尔玫瑰图+图例
  8. 自控原理学习笔记-系统稳定性分析(1)-BIBO稳定及Routh判据
  9. 牛牛牛!正则阿拉伯数字变中国大写
  10. Fovea Box阅读学习笔记