你所不知道的补码

某君不无兴奋地跟我说他有个重大发现: java里最小负整数的绝对值等于它本身! 程序员朋友可以试试以下判断:

Math.abs(int.MIN_VALUE) == int.MIN_VALUE

不管怎样, 取绝对值后一定是个正数, 怎么可能等于一个负数呢.乍一看, 太二和尚摸不着头脑. 向来以精准著称计算机世界, 怎么会得出如此可笑的结论?

其中一定有什么隐情! 凭直觉, 这应该和补码有关. 补码, 初中时代的知识, 不出意外地, 忘得差不多了, 先温故一下, 没准能知新.

正数的补码等于它自己, 负数的补码等于其反码+1.

计算机是以二进制方式表示数字的, 其中负数又以补码的方式表示. 当回忆起这点的时候, 有一个问题吸引了我:为什么负数要用补码表示呢?, 这似乎是一个和1+1为什么等于2一样傻的问题, 所以当年聪明的我没敢问老师为什么. 如今智商越发下降, 类似的问题也越来越多, 然后, 像小孩子发现更好玩的玩具一样, 开头那个问题就被晾在一边了…

大概一开始人们也没设计出补码这玩意儿, 毕竟谁会上来就找麻烦搞这么复杂的东西. 应该是很自然地第一位是符号位, 0表示正数, 1表示负数, 剩下是表示值的多少. 当然, 对于人类轻而易举想到的答案, 上帝一般是要发笑的. 这个方案会有什么问题呢? 让我们简单地假定整数是用3个比特表示的, 其表示的所有数字, 排列组合如下:

000: 0, 001: 1, 010: 2, 011: 3, 100: -0, 101: -1, 110: -2, 111: -3

这样也能work. 但很显然的不足是, 0有两种表示法, 有点浪费空间了. 还有一个更隐蔽的缺点, 用一道题来说明. 按上述规则计算下1-1, 也就是1+(-1)的结果.

001+101=110

110就是-2, 显然不对. 那么如何设计才能让同时弥补上述两个缺点呢? 这时候, 补码就隆重登场了. 按补码的规则重新穷举上述排列:

000: 0, 001: 1, 010: 2, 011: 3, 100: -4, 101: -3, 110: -2, 111: -1

0不再重复了, 表示的空间从[-3, 3], 扩大到[-4, 3]. 而且1-1的计算, 变得简单自然, 丝般顺滑(最高位溢出):

001+111=000

因此, 不必为加法和减法设计两套计算电路! 两个看似不同, 甚至对立的逻辑, 得到了统一. 设计之巧妙, 令人叹服. 看似很傻的问题, 细细研究, 原来这般微妙, 优雅得简直像艺术品, 不得不佩服提出补码那位兄台.

绕了个圈, 回到最开始的问题. 绝对值的计算大家都清楚: 正数, 直接返回它自己, 负数, 返回其相反数. 而java计算相反数, 就是取其补码. 仍然拿3比特的为例, 最小的负数是100(-4), 取反是011, 加1后是100, 还是-4. int类型一般是32比特, 但演算过程是一样的.

写代码如何能少出bug? 做架构如何能更灵活适应需求的变化? 一个共同的答案是, 发现和归纳事物的规律, 通过巧妙的设计, 或更高的抽象, 减少差异性, 提高普适性.

设计行业里流行少即是多的理念. 我想, 少不是刻意删除, 不是空洞无物. 少, 是在充分理解的前提下, 找到那个支点, 四两拨千斤, 用最小的付出, 撬动最大的收益. 九九归一, 少得下来, 一定是看透了规律, 抓住了本质.

上面对补码的理解, 足够概括和普适了吗? 并没有.

先试试在进制上做点延展. 小学生都会的减法, 能否统一成加法呢? 答案是肯定的. 事实上,

两整数 A、B 用同一个正整数 M (M 称为模)去除而余数相等,则称 A、B 对 M同余,记作: A=B (MOD M). 具有同余关系的两个数为互补关系,其中一个称为另一个的补码.

那么, 一个更加概括的理解是:减去一个数, 等于加上这个数的补码.

数学源于生活, 让我们举个日常生活的例子. 时钟连3岁小孩都能看懂, 比11点早3小时是8点, 比11点晚9小时也是8点, 嘿嘿, 说的就是这个理, 只不过这里的模是12. 而计算机用的是二进制, 以2为模: 1-1 = 1+1 = 2+0 = 0 (2被抹掉了).

所以下回教小孩子做减法可以这么说: 7-3 = 7+7 = 10+4 = 4. 当然, 要解释为什么10被任性地抹掉了, 也挺费劲.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 网站乱码问题我们会经常碰到,大多见于非英文的中文字符或其他字符乱码,而且,这类问题常常是因为编码方式问题,主要原因...
    波段顶底阅读 2,957评论 1 9
  • 本篇文章讲解了计算机的原码, 反码和补码. 并且进行了深入探求了为何要使用反码和补码, 以及更进一步的论证了为何可...
    yang2yang阅读 2,283评论 1 13
  • 前些日子,抽空做了一下所谓的微3D效果的控件,然后我给他取名叫JECalourseView,由于本人比较忙,比比较...
    一个野指针阅读 3,444评论 4 8
  • 图/木槿 文/木槿 春花秋月 夏日冬雪 极美、醉人情 一轮逍遥游历 来日暖席煮酒 索然心事愁欢 痴缠千幕景 留恋万...
    痴人一念阅读 264评论 2 18
  • 一个女人的自述 我想我就是个名副其实的“破鞋”吧。从我毕业到现在八年了,我有过六个男朋友,还有一个旅行是遇见的“艳...
    可怜人Loser阅读 8,507评论 2 0