【JAVA基础11】—— 吃透原码、反码、补码:计算机数值表示的底层逻辑

一、为什么需要原码、反码、补码?

在计算机的世界里,所有数据最终都以二进制形式存储。但二进制如何表示负数?如何让加减法运算统一(毕竟计算机本质上只会做加法)?这就催生了原码、反码、补码的概念 ------ 它们是解决 "带符号数存储与运算" 的三套核心方案,层层递进地解决了原码的运算缺陷。

先明确一个前提:计算机存储数据的位数是固定的(如 8 位、16 位、32 位),我们以最常用的 8 位二进制为例,最高位(左边第一位)是 "符号位":0 表示正数,1 表示负数,其余 7 位是 "数值位",表示数字的绝对值。

二、原码:最直观的二进制表示

定义

原码(True Form)是最贴近人类认知的表示方式:符号位 + 数值的绝对值二进制。

  • 正数原码:符号位为 0,数值位直接写绝对值的二进制。

  • 负数原码:符号位为 1,数值位写绝对值的二进制。

实例(8 位)
十进制数 原码(8 位二进制) 说明
+5 0000 0101 符号位 0,数值位 000 0101(5 的二进制)
-5 1000 0101 符号位 1,数值位 000 0101
+0 0000 0000 符号位 0,数值位全 0
-0 1000 0000 符号位 1,数值位全 0
原码的问题

看似直观,但加减法运算会出错!比如计算 1 + (-1):

  • 1 的原码:0000 0001

  • -1 的原码:1000 0001

  • 相加结果:1000 0010(对应十进制 - 2),显然违背常识。

  • 额外缺陷:存在 "+0" 和 "-0" 两个表示,浪费存储空间。

三、反码:为解决原码运算缺陷而生

定义

反码(One's Complement)是原码的 "镜像" 修正,核心规则:

  • 正数反码:与原码完全相同(符号位 0,数值位不变)。

  • 负数反码:符号位保持 1,数值位按位取反(0 变 1,1 变 0)。

实例(8 位)
十进制数 原码 反码 说明
+5 0000 0101 0000 0101 正数反码 = 原码
-5 1000 0101 1111 1010 数值位 0101 取反为 1010
+0 0000 0000 0000 0000 正数反码 = 原码
-0 1000 0000 1111 1111 数值位全 0 取反为全 1
反码的改进与残留问题

反码解决了部分运算问题,比如计算 1 + (-1):

  • 1 的反码:0000 0001

  • -1 的反码:1111 1110

  • 相加结果:1111 1111(对应反码的 - 0),虽然逻辑上 "0" 的表示统一了(运算结果无错误),但仍存在两个关键问题:

  1. 仍有 "+0" 和 "-0" 的区分(反码中 0000 0000 是 + 0,1111 1111 是 - 0),未彻底解决存储浪费。

  2. 减法运算需额外处理 "进位":若运算结果超过 8 位(溢出),需将进位位 "循环加回" 最低位,增加硬件复杂度。

四、补码:最终被广泛采用的标准

定义

补码(Two's Complement)是反码的最终优化,彻底解决了前两者的缺陷,核心规则:

  • 正数补码:与原码、反码完全相同(符号位 0,数值位不变)。

  • 负数补码:符号位保持 1,数值位按位取反(即反码)后,末位加 1(若有进位则顺延)。

实例(8 位)
十进制数 原码 反码 补码 说明
+5 0000 0101 0000 0101 0000 0101 正数补码 = 原码 = 反码
-5 1000 0101 1111 1010 1111 1011 反码 1111 1010 末位加 1 得 1111 1011
+0 0000 0000 0000 0000 0000 0000 正数补码 = 原码
-0 1000 0000 1111 1111 0000 0000 反码 1111 1111 末位加 1,溢出后全 0
补码的核心优势(为什么成为标准?)
  1. 彻底消除 "-0":-0 的补码计算后为 0000 0000,与 + 0 统一,节省 1 个存储单元。

  2. 加减法统一为加法 :负数的补码本质是 "模运算" 的结果,使得 a - b = a + (-b) 可直接通过二进制加法实现,无需额外硬件逻辑。

  • 示例:计算 5 - 3(即 5 + (-3))

    • 5 的补码:0000 0101

    • -3 的补码:1111 1101(原码 1000 0011 → 反码 1111 1100 → 末位加 1 得 1111 1101)

    • 相加结果:0000 0010(补码),对应十进制 2,结果正确。

  1. 无进位循环问题:补码运算中,溢出的进位直接丢弃即可,不影响结果正确性(因补码的模运算特性)。

五、原码、反码、补码的核心区别总结

特性 原码 反码 补码
正数表示 符号位 0 + 数值位 符号位 0 + 数值位 符号位 0 + 数值位
负数表示 符号位 1 + 数值位 符号位 1 + 数值位取反 符号位 1 + 反码末位加 1
"0" 的表示 +0(00000000)、-0(10000000) +0(00000000)、-0(11111111) 唯一 0(00000000)
运算逻辑 加减法需分开处理 加减法统一,但需进位循环 加减法统一,溢出进位丢弃
实际应用 几乎不用(仅用于直观表示) 极少使用(过渡方案) 计算机存储 / 运算的标准

六、一句话记住核心逻辑

  • 正数:原码 = 反码 = 补码(符号位 0,数值位不变);

  • 负数:反码是原码 "数值位取反",补码是反码 "末位加 1";

  • 补码的本质:用 "模运算" 让负数变成 "可加的正数",解决计算机只能做加法的底层限制。

相关推荐
阿蒙Amon2 小时前
C#常用类库-详解Playwright
开发语言·c#
特种加菲猫2 小时前
C++ std::list 完全指南:从入门到精通所有接口
开发语言·c++
清空mega2 小时前
第4章:JSP 程序设计实战——for、if、动态表格与 99 乘法表
开发语言·python
共享家95272 小时前
Java入门(类和对象)
java·开发语言
习惯就好zz2 小时前
Qt Quick 系统托盘完整实践
开发语言·qt·qml·系统托盘·system tray·qapplication·qguiapplication
Arya_aa2 小时前
Java中的static
java
笨笨马甲2 小时前
Qt集成OpenCV
开发语言·qt
笨笨马甲2 小时前
Qt 工业机器视觉开发
开发语言·qt
咚为2 小时前
深入浅出 Rust FFI:从内存安全到二进制兼容
开发语言·安全·rust