新闻详情

新闻详情

首页 / 资讯中心 / 详情

原码、反码、补码:从“背规则”到“推规则”的彻底理解方法

发布时间:2026/9/27 23:20:26来源:尧图网络
原码、反码、补码:从“背规则”到“推规则”的彻底理解方法
原码、反码、补码真的理解透了才不会“背了就忘”我当年学计算机组成原理的时候和你一模一样——原码、反码、补码的定义背得滚瓜烂熟正数三者相同负数反码是原码符号位不变、其余位取反补码是反码加1。但说实话背完不到两周遇到“-105的补码用16位表示是多少”这种题我还是要翻书推一遍。为什么因为我在背“规则”没理解“规则”是怎么来的。后来我花了整个周末把“模”“同余”“时钟回拨”这些数学底子翻出来把补码的来历捋了一遍才发现这东西根本不是靠背的它就是一套“圆盘对齐”的数学游戏。从那以后不管是8位、16位还是32位给我一个负数算补码我再也没翻过书。这篇文章不打算把教科书内容念一遍而是把我“从死记到理解”的那条路线完整走一遍。你会看到为什么计算机要引入补码、原码反码到底卡在哪、补码为什么是取反加1、以及-105这类数怎么在16位下又快又不出错地算出来。文章较长但看完之后这三种码对你来说就再也不是三行需要背的规则而是一套可以随手推出来的东西。1. 为什么计算机不直接“用原码做减法”1.1 符号位带来的第一个麻烦先看原码。原码的思想很朴素一个数用“符号位 绝对值”表示最高位是0表示正1表示负。比如用8位表示5的原码0000 0101-5的原码1000 0101看着挺直观符号位加绝对值嘛。但问题马上就来了如果让计算机用原码直接计算 5 (-5)会得到什么0000 0101 (5) 1000 0101 (-5) 1000 1010 (-10)结果居然等于-10。这不是我要的0。问题出在哪出在计算机只会按二进制位做加法它不理解什么是“符号位”。在它眼里1 1 就是进位1000 0101 的最高位1也只是个参与运算的普通比特而已。于是原码做加减法还得专门设计一套“先判断符号再看谁大谁小决定加还是减最后定符号”的逻辑。说白了机械地把符号位和数值位拆开处理硬件就要为此付出代价——加法器之外还得再配一堆判断电路又慢又容易错。这就是第一个麻烦符号位本身不参与运算导致原码的加减法根本不能直接算。那能不能让符号位也参与运算能但得换一种编码方式。1.2 现实世界的“模”和“同余”在讲补码之前先说一个所有人都很熟但没拿来用的生活常识时钟。一个12小时的钟表从3点开始如果想拨到1点你可以逆时针拨2小时3 - 2 1也可以顺时针拨10小时3 10 1313点就是1点。在12小时制的表盘上-2和10的效果完全一样都落在1点的位置。这里的“12”就叫做模Modulo。数学上13和1对模12是同余的写作13 ≡ 1 (mod 12)。换句话说在一个模为12的系统里“减去2”和“加上10”是同一件事。而“加上10”就是“减去2的补数”。计算机里的数也一样如果寄存器的位宽是n位它能表示的数值总数就是2^n这个2^n就是模。在模2^n的体系里我们没有“减法器”只有“加法器”那么“减一个数”就可以等价地变成“加上这个数的补数”。问题的关键只剩一个对于一个负数它的补数到底是多少1.3 补码就是把“减法”包装成“加法”还是以8位为例模是256。-5想要变成“加上某个正数”完成等价效果就是找一个X使得-5 ≡ X (mod 256)移个项X ≡ 256 - 5 251。也就是说在8位系统里-5的补码形式应当就是251这个数对应的二进制——1111 1011。你验证一下5 (-5) 用补码算就是 0000 0101 1111 1011 1 0000 0000。注意这里产生了一个第9位的进位1而8位寄存器只保留低8位结果是0000 0000恰好等于0。看见没有符号位参与运算了减法变成加法了最后进位丢掉就得到正确答案。这就是补码的核心价值计算机只需要一套加法器就能同时搞定加法和减法。这个设计上的简洁直接影响了今天所有CPU的ALU电路结构。对比一下原码要额外判断符号的做法高下立判。2. 三种编码的完整定义与求法2.1 原码最直观但问题最多原码就是“符号位 真值的绝对值”。用8位来举例70000 0111-71000 0111它的优点是人的认知成本最低写出来一眼就能看出数的大小。缺点是除了1.1里提到的做减法会出错之外还存在一个非常别扭的问题——“0”有两种表达0000 0000表示01000 0000表示-0。同一个数有两个二进制形态这让“判断一个数是否等于0”这种最基本的操作都变得复杂起来。计算机里判断a 0只需要检查所有位是否为0但原码体系下你还要额外判断“是不是-0”直接让硬件逻辑复杂一截。所以原码现在很少直接用于运算更多是作为“人类可读的中间展示形态”存在比如某些协议里传输浮点数的符号位和尾数时仍然能看到它的影子。2.2 反码补码的“半成品”反码的定义很暴力正数的反码等于原码负数的反码是“原码的符号位保持不变其余位全部取反”。还是用-7举例7 原码0000 0111反码也是 0000 0111-7 原码1000 0111反码就是 1111 1000问一个问题为什么要有反码这个东西直接跳到补码不行吗从定义能看出来反码是“通往补码的一个中间步骤”——因为补码等于反码加1。但反码自己其实也解决了一部分运算问题如果用反码做减法符号位同样可以参与运算。比如 7 (-7) 用4位反码算一下这里用4位是为了方便观察模是160111 (7) 1000 (-7的反码) 1111 (-0的反码)结果确实是0但长得像-0而不是0。反码体系里仍然存在“0”和“-0”两个零的问题。如果计算结果的位模式是1111处理器要做一次额外处理才能把它修正为真正的0。这还不是最要命的反码里从1111-0加1得到0000真正的0需要一次进位但这个进位在4位体系里被丢弃了。这导致反码做加法时如果最高位有进位你还得把它加回最低位这种操作叫“循环进位”否则结果会差1。你想想硬件为了处理这个“循环进位”又得加多少逻辑。反码证明了“符号位参与运算”这条路是走得通的但它走得磕磕绊绊。补码就是在反码基础上向前一步把-0这个位置让给了一个更小的负数从而彻底抹平了“有两个零”的怪相。2.3 补码取反加一从定义到速算补码的定义有很多等价写法但最好理解的一句话是负数的补码等于模减去该负数的绝对值。以8位为例模是256-7的补码就是 256 - 7 249二进制是1111 1001。但问题来了总不能每次算负数补码都去心算256减几吧所以大家总结了那个经典的快速求法先写出该负数绝对值的原码然后“符号位不变、数值位取反”最后再加1。也就是“取反加1”。以8位-7为例绝对值7的原码0000 0111 按位取反 1111 1000 再加1 1111 1001得到的1111 1001和256-7249的二进制完全一致。这说明“取反加1”这个速算规则不是天上掉下来的它是基于“模 - 绝对值”这个本质推出来的便捷算法。我还想给你一个“反着求”的对称技巧已知一个负数的补码想快速求它对应的绝对值方法是“再取反加1”。比如1111 1001取反得到0000 0110加1得到0000 0111也就是7。于是我们知道1111 1001表示-7。这是一个很实用的逆运算后面在做调试、逆向、查看内存里的负数时都会频繁用到。2.4 三种编码对照表一眼看清区别把同一个数值在8位下的三种编码放在一起看数值原码反码补码70000 01110000 01110000 0111-71000 01111111 10001111 100100000 00000000 00000000 0000-01000 00001111 1111不存在的概念被-128占用-11000 00011111 11101111 1111-12811000 0000需要9位才能按原码写全无法表示-0被占1000 0000表格里有个非常关键的信息补码体系里没有“-0”原来干-0的那个位置变成-128。这也是为什么8位补码能表示的范围是-128到127而不是像原码反码那样只有-127到127。多出来的那个数就是取消-0之后捡到的“福利”。3. 理解补码的底层逻辑从取反加1到最高位3.1 为什么“取反加1”在数学上等价于模运算如果你只记住“取反加1”不理解背后的等价性那遇到下面这题还是会慌-105的补码用16位表示是多少要稳住不能死记要从原理推。一个关键数学事实对一个n位二进制数x按位取反得到的结果加上x本身每一位都会变成1也就是得到2^n - 1。举个例子8位里0000 0111取反是1111 1000两者相加0000 0111 1111 1000 1111 1111而1111 1111就是2^8 - 1 255。于是得到恒等式x (~x) 2^n - 1两边同时加1x (~x 1) 2^n也就是说任何数x如果把它取反再加1记为~x 1那么x加上这个结果刚好等于模2^n。按照模运算的规则这意味着~x 1就是x的“模补数”。特别地当x是一个正数的绝对值时~|x| 1就是这个负数的补码表示。整个过程不需要任何“符号位”的概念纯粹是二进制运算的自然结果。这就是为什么“取反加1”能算补码因为它就是在计算“模 - 绝对值”。3.2 把补码看成一个大圆盘上的刻度我再换一种更直观的方式来解释补码想象一个车轮一圈有2^n个刻度。把0放在最顶上然后按顺序一圈排开0、1、2、…、2^(n-1)-1、2^(n-1)再往下转就是2^(n-1)1一直转到2^n-1也就是“差一格就回到0”的位置。补码的巧妙之处在于我们重新解释这些刻度把后一半即最高位为1的那些看成负数0000 0000 到 0111 1111表示0到1271000 0000表示-128这个数很特别它既没有正数对应也没有-0对应1000 0001 到 1111 1111表示-127到-1这样-1就落在“差一格就回到0”的刻度上也就是1111 1111。从-1往前走一格变成0000 0000刚好回到0这和不带符号的255 1 0溢出丢弃进位完全一致。所以补码本质上就是把一个“无符号0到2^n-1的环”折叠成了“-2^(n-1)到2^(n-1)-1的环”。在这个环上加法和减法都是沿着圆周走永远走不出这个环。3.3 一个关键结论补码的最高位是“天然的符号位”很多人学补码时觉得既然补码的符号位参与运算了那它还有没有符号位有而且依然是最左边那位只不过它不只是“符号指示”它是数值的一部分。我们可以观察一个规律如果补码的最高位是0这个数落在0到2^(n-1)-1的正数区间如果最高位是1这个数落在-2^(n-1)到-1的负数区间。所以“最高位为1”天然就是负数的标志。这一点在实际编程里非常重要。比如你调试程序时查看一段内存看到一个unsigned short值是65431十六进制是0xFF97。如果你知道它其实被当成short来用那么最高位是1说明是个负数。65431 - 65536 -105于是你立刻知道0xFF97表示的是-105。这种“无符号视角和补码视角互转”的手感很多工作第5年的人都不一定有但其实就是这个最高位规则的应用。4. 实操-105的补码用16位表示一步步算给你看4.1 先算105的二进制再统一位宽现在来正面处理标题热词里的那道题-105的补码用16位表示。先说结论是 1111 1111 1001 0111。接下来看怎么算以及为什么是这个结果。步骤一把绝对值105写成二进制。105 64 32 8 1对应二进制就是 1101001。补齐到16位写成 0000 0000 0110 1001。补高位时注意要在左边补0不是右边这个顺序反了整题就废了。步骤二取反。每一位0变1、1变0得到 1111 1111 1001 0110。步骤三加1。从最低位开始算1111 1111 1001 0110 1 1111 1111 1001 0111注意最低位0加1变成1没有向高位进位所以上面的位都不受影响。最后结果是 1111 1111 1001 0111。再验证一下用“模 - 绝对值”法16位的模是65536-105就是 65536 - 105 65431。把65431转成二进制就是 1111 1111 1001 0111。两条路线得到同一个结果说明算对了。4.2 用“无符号数值”验证有一个判断补码算得对不对的好方法把补码当成无符号整数来看它的值应当等于 65536 - 105。只要满足“补码的无符号值 该数的绝对值 2^n”结果就是对的。如果手边有计算器可以这样验证把 1111 1111 1001 0111 转成十进制高位是0xFF低位是0x97所以整体是0xFF97 65431。65431 105 65536 2^16。达标。这个验证法在考试、面试、实际编码里都非常好用因为你不用重新推一遍只要做一次无符号转十进制加法。4.3 8位与16位位宽不同补码长得完全不同同样的-105如果要求的是8位补码会是什么105的8位二进制是0110 1001取反得到1001 0110加1得到1001 0111。所以8位补码是1001 0111而16位补码是1111 1111 1001 0111。这两个结果很容易混。很多人看到16位补码里出现了8位补码的样子就以为16位补码就是把8位补码前面补0——那就大错特错。负数用更多位表示时前面补的是1不是0。这个过程叫符号扩展sign extension原本的最高位是符号位1扩展到16位时在左边补1保持数值不变。这也是为什么我在第4.1节强调“先把105写成16位二进制再取反加1”而不是“先求8位补码再扩展”。前者不容易出错后者容易让人糊涂-105的8位补码是1001 0111如果误以为扩展就是高位补0会得到0000 0000 1001 0111按无符号数看是151按补码看是真值151——完全不是-105。记住负数扩展补1正数扩展补0。4.4 实操演示从十六进制到十进制的心算路线再看一遍实际工程里的用法。比如调试器里显示一个16位寄存器值是0xFF97。我看到0xFF97脑中第一反应是把它拆成0xFF97是无符号数等于65535 - 104 65431。为什么是104因为0xFF97 0xFFFF - 0x00680x0068 104。65535 - 104 65431。于是按16位补码解释65431 - 65536 -105。这条“补码无符号值 - 2^n 真值”的路线在许多编程场景下比重新取反加1快得多。如果你频繁接触底层调试、协议解析或者做嵌入式寄存器操作强烈建议把它练成条件反射。5. 常见错误与面试/考试高频坑5.1 “-0”去哪了为什么8位补码下限是-128前面说过原码和反码都有“-0”问题而补码直接把正0保留为0000 0000把原来-0的位置1000 0000留给了-128。所以8位补码的范围是-128到127。这个“范围不对称”经常让人困惑为什么负数多一个逻辑很简单——补码里没有-0多出来的那个编码当然就往下界走。行话叫“补码的表示范围是-2^(n-1)到2^(n-1)-1”。记住这个公式不管8位、16位、32位都能立刻报出范围。顺便说“-128的8位补码是1000 0000”这道题也经常出现在面试里。很多人按“取反加1”去求会卡住因为128的8位原码写不出来8位原码最多表示到127。这时候要回到定义-128的补码 256 - 128 128所以是1000 0000。用“模”去求永远不会被“原码写不下”卡住。5.2 符号扩展和位宽不一致的隐患前面举的-105例子已经展示了符号扩展的要点。再做一道易错题把8位的-11111 1111扩展成16位应该是1111 1111 1111 1111而不是0000 0000 1111 1111。后者的真值是255完全错误。这里有个常见陷阱在C语言里如果char类型的-1被强制转换成int由于char是有符号类型编译器会自动做符号扩展结果是正确的-1。但如果你先把char转成unsigned char再转int就会得到0x000000FF也就是255。很多人在做网络协议报文解析时把一个字节当成uint8_t读取后直接和int比较或参与运算如果那个字节最高位是1就会踩到符号扩展的坑。实际排查时这种“莫名奇妙的255”往往就是符号扩展没做对。5.3 溢出判断为什么“两个正数相加变负数”不是bug而是溢出补码运算里最经典的肉眼可见的“错误”是127 1 在8位补码下变成 -128。0111 1111 (127) 0000 0001 (1) 1000 0000 (-128)从数学上看127 1 128但8位补码表示不了128它跑到-128去了。这不是运算错而是溢出结果超出了表示范围进位进入了符号位。日常编程中这就是int型溢出的一种有符号整数溢出在C/C里属于未定义行为在Python里不存在Python整数无限精度但在Java里会静默回绕。理解补码以后你就能明白为什么很多库函数要在做加法前用“a INT_MAX - b”这种方式提前判溢出而不是等加了以后再看正负。5.4 快速排查表症状可能原因怎么办负数扩展后变大把符号扩展做成了零扩展负数高位补1只有正数高位才补08位补码里出现-0或128混乱对“取反加1”机械套用没走模运算用2^n - 绝对值来定义再对照速算规则两个正数相加结果变负结果溢出进入符号位提前判断是否超出表示范围十六进制转十进制后数值巨大无符号视角看补码无符号值大于2^(n-1)时减去2^n得到真值原码反码补码背了又忘没有建立模与同余的认知用钟表类比重新推一遍“取反加1”的来历5.5 一个快速心算技巧负数补码也可以“从右往左找第一个1”如果不想每次都用“取反加1”来算负数补码还有一个很妙的口算方法保持最低位往右直到第一个1不变这个1后面的所有高位全部取反。举个例子-105。先看绝对值105的二进制0110 1001。从右往左找第一个1最低位就是1105是奇数所以最低位保持1不变其余高位全部取反0110 1001 - 变为1001 0111。得到8位补码1001 0111。是不是和前面的结果完全一致这个技巧在内心里算负数补码特别快尤其是要求“完整写出位模式”的填空题。原理还是“取反加1”取反加1会让最低位的连续0变成1而第一个1及其右边的位在加1后都不会变左边则整体取反。用这个视角“取反加1”就不再是两步操作而是“找到了第一个1一刀切左边取反右边保留”。练熟以后口算补码速度能快一倍。6. 从原理到代码补码在实际编程中的应用观察6.1 判断奇偶与符号位读取很多人写代码判断一个整数是不是奇数用i % 2。但更底层的做法是看最低位i 1。这个技巧和补码有什么关系有因为在补码表示下负数的最低位同样承载“是否为1”的信息取模对负数来说结果可能为负比如-3 % 2 -1但按位与1永远是0或1。这就是了解底层表示法对日常开发的直接好处。符号位读取也一样i 31在32位补码下正数得到0负数得到-1所有位都是1。用(i 31) 1才能提取出符号位本身。如果想写一个“返回绝对值的分支”等代码熟悉这个位模式能让你看懂许多高效但难读的写法。6.2 无符号视角下的递减当你在C语言里写for (unsigned int i 10; i 0; i--)时这个循环永远不会结束。因为i减到0后再减1就变成0xFFFFFFFF也就是4294967295它依然大于等于0。这个bug的“罪魁祸首”就是补码统一了加法和减法后底层没有“负数”这个概念0xFFFFFFFF作为无符号数就是很大的正数。理解补码之后这类“死循环”就特别容易诊断要么用i 0要么改用有符号类型要么用do-while结构。6.3 位宽截断与回绕给一个8位变量赋256结果变成0赋257变成1。这是因为低8位截断后保留的是补码或无符号的低位模式。很多加密、校验、哈希算法里故意依赖这种“截断回绕”行为来计算结果模2^n的值。如果你不理解补码和模的本质看到这类代码经常一头雾水但只要理解“一切都在模2^n的环上转圈”这些运算就都顺了。7. 我自己在踩坑之后总结的几条体会在做底层开发的那几年里我在一个网络协议调试中度过了好几个凌晨。当时抓包发现某个字段总是比预期值大65536倍排查到最后问题就出在一个16位有符号数被当成无符号数使用了。从那以后我养成了一个习惯看到任何十六进制数据先问自己一句“这个字段到底是有符号还是无符号位宽是多少”。这句话看起来简单但能免掉太多无头苍蝇式的排查。如果早把补码和符号扩展的坑理解透那天我根本不用熬到那么晚。最后送给你一个小练习随便挑一个负数比如-89分别求它的8位补码、16位补码再用十六进制写出来。然后把这个十六进制换成十进制无符号数验算一下它加89是不是65
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

揭阳百度推广优化避坑指南:5个免费工具提升30%转化 2026/9/28 0:08:50

揭阳百度推广优化避坑指南:5个免费工具提升30%转化

揭阳百度推广优化避坑指南:5个免费工具提升30%转化 改个需求建站公司拖一周?别忍了。很多揭阳老板觉得百度推广难搞,其实是被“黑箱”操作坑了。今天直接甩出5个 免费工具 ,教你自己盯数据,不再当冤大头。 运营目标与指标:别只看点击量…

阅读更多 →
5个坑讲透wordpress文章自动发布功能避坑指南 2026/9/28 0:08:25

5个坑讲透wordpress文章自动发布功能避坑指南

5个坑讲透wordpress文章自动发布功能避坑指南 备案流程一头雾水,很多新手在配置服务器时就卡住了,以为只要把代码传上去就能跑,结果发现文章定时发布功能死活不生效。这时候你需要的是一份 避坑指南…

阅读更多 →
3个关键维度教你怎么选软件下载网站地址 2026/9/28 0:07:59

3个关键维度教你怎么选软件下载网站地址

3个关键维度教你怎么选软件下载网站地址 备案流程一头雾水?别慌,选错地址直接卡死。很多创业团队负责人盯着域名发呆,其实【怎么选】才是核心。今天用3个维度拆解【软件下载网站地址】,避开90%的坑。 域名后缀决定备案生死…

阅读更多 →
3个实战技巧让wordpress流量插件数据翻倍新手入门必看 2026/9/28 0:07:53

3个实战技巧让wordpress流量插件数据翻倍新手入门必看

3个实战技巧让wordpress流量插件数据翻倍新手入门必看 自己不会代码想做网站,是不是看着后台那些复杂的设置就头大?别慌,很多新手入门时都卡在这一步。其实,wordpress流量插件的核心不在于你懂多少代码,而在于你如何用最简单的配置,…

阅读更多 →
怎么在阿里云建网站:告别模板,3步搞定保姆级建站教程 2026/9/28 0:07:46

怎么在阿里云建网站:告别模板,3步搞定保姆级建站教程

怎么在阿里云建网站:告别模板,3步搞定保姆级建站教程 还在忍受那些千篇一律、配色刺眼且毫无品牌感的模板网站吗?很多老板一上来就买现成模板,结果上线后发现客户觉得“廉价”,自己看着也闹心,完全撑不起企业的专业形象。其实,真正能留住客户、体现实…

阅读更多 →
电子商务网站建设的结论对比评测 2026/9/28 0:07:39

电子商务网站建设的结论对比评测

电商建站避坑:最佳实践总结与运维实战 改个需求建站公司拖一周,后台数据还乱得像一团麻?这种憋屈感,我猜很多老板都经历过。别再被销售话术忽悠了,电子商务网站建设的结论核心不在于“看起来多花哨”,而在于底层架构是否稳固、运维是否透明。今天咱们不…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉