原码、反码、补码:计算机中表示有符号整数的方式及应用

笔记哥 / 04-30 / 48点赞 / 0评论 / 669阅读
目录 - **1. 原码(Sign-Magnitude)** - **定义** - **特点** - **使用场景** - **2. 反码(Ones' Complement)** - **定义** - **特点** - **使用场景** - **3. 补码(Two's Complement)** - **定义** - **特点** - **使用场景** - **4. 为什么需要反码和补码?** - **(1)解决 `+0` 和 `-0` 问题** - **(2)统一加减运算** - **(3)硬件优化** - **5. 实际应用示例** - **(1)Java 中的 `byte` 类型** - **(2)无符号转换** - **(3)网络协议 & 文件解析** - **6. 总结** - 示例 - **为什么 在Java中 `0xFC` 变成 `-4`?** - **示例代码** - **如何正确查看 `0xFC`?** - **关键点总结** - **为什么 Java 的 `byte` 是有符号的?** - **适用场景** 在计算机中,**原码、反码、补码** 是表示有符号整数的三种方式,主要用于解决 **负数存储和运算** 的问题。它们的定义、作用及使用场景如下: * * * ## **1. 原码(Sign-Magnitude)** ### **定义** - **最高位表示符号**(`0` 为正,`1` 为负),其余位表示数值。 - 例如,8 位二进制: - `+5` 的原码:`0000 0101` - `-5` 的原码:`1000 0101` ### **特点** - 直观,人类容易理解。 - **问题**: - **`+0` 和 `-0` 不唯一**(`0000 0000` 和 `1000 0000`)。 - **加减运算复杂**,需要额外判断符号位。 ### **使用场景** - 早期计算机(如 IBM 701)使用原码,但现代计算机基本不再使用。 * * * ## **2. 反码(Ones' Complement)** ### **定义** - **正数**:反码 = 原码。 - **负数**:符号位不变,其余位 **按位取反**。 - 例如: - `+5` 的反码:`0000 0101`(同原码) - `-5` 的反码:`1111 1010`(符号位 `1`,数值位取反) ### **特点** - 解决了 `+0` 和 `-0` 的问题(反码下 `-0` 是 `1111 1111`,但仍有冗余)。 - **缺点**: - 加减运算仍需处理进位(如 `-5 + 5 = 1111 1010 + 0000 0101 = 1111 1111`,即 `-0`)。 - 硬件实现复杂。 ### **使用场景** - 早期计算机(如 PDP-1)使用反码,但已被补码取代。 * * * ## **3. 补码(Two's Complement)** ### **定义** - **正数**:补码 = 原码。 - **负数**:反码 + 1(即 **取反后加 1**)。 - 例如: - `+5` 的补码:`0000 0101`(同原码) - `-5` 的补码: ```csharp 原码:1000 0101 反码:1111 1010 补码:1111 1011 (反码 + 1) ``` ### **特点** - **解决了 `+0` 和 `-0` 问题**(补码下 `-0` 表示为 `0000 0000`,与 `+0` 相同)。 - **加减运算统一**(直接按二进制加法计算,无需额外处理符号位)。 - **硬件实现简单**(只需加法器,无需额外电路)。 ### **使用场景** - **现代计算机(包括 Java)全部使用补码存储有符号整数**。 - 例如: - Java 的 `byte`、`short`、`int`、`long` 均用补码表示。 - `0xFC`(`1111 1100`)在 `byte` 类型中表示 `-4`(因为补码计算:`~1111 1100 + 1 = 0000 0100`,即 `-4`)。 * * * ## **4. 为什么需要反码和补码?** ### **(1)解决 `+0` 和 `-0` 问题** - 原码和反码中,`+0` 和 `-0` 的表示不同,导致比较和运算复杂。 - 补码中 `0` 只有一种表示(`0000 0000`),简化逻辑。 ### **(2)统一加减运算** - 补码下,**减法可以转换为加法**(`A - B = A + (-B)`),硬件只需加法器。 - 例如:`5 - 3 = 5 + (-3)`: ```csharp 5 的补码:0000 0101 -3 的补码:1111 1101 相加:0000 0101 + 1111 1101 = 0000 0010 (即 2) ``` ### **(3)硬件优化** - 补码运算无需额外判断符号位,减少电路复杂度。 * * * ## **5. 实际应用示例** ### **(1)Java 中的 `byte` 类型** ```java byte b = (byte) 0xFC; // 0xFC = 1111 1100(补码) System.out.println(b); // 输出 -4(因为补码 1111 1100 表示 -4) ``` ### **(2)无符号转换** 由于 Java 没有无符号 `byte`,需用 `& 0xFF` 转换: ```java int unsignedValue = b & 0xFF; // 0xFC(252) System.out.println(unsignedValue); // 输出 252 ``` ### **(3)网络协议 & 文件解析** - 读取 TCP/IP 报文、二进制文件时,数据可能是补码形式,需按补码解析: ```java // 从文件读取 2 字节的 short(补码存储) short value = (short) ((bytes[0] << 8) | (bytes[1] & 0xFF)); ``` * * * ## **6. 总结** | 表示方式 | 定义 | 优点 | 缺点 | 使用场景 | | --- | --- | --- | --- | --- | | **原码** | 符号位 + 绝对值 | 直观 | `±0` 问题,运算复杂 | 早期计算机 | | **反码** | 负数:符号位 + 取反 | 解决 `±0` 问题 | 运算仍有进位问题 | 过渡方案(如 PDP-1) | | **补码** | 负数:反码 + 1 | 统一加减法,硬件简单 | 无 | 现代计算机(Java/C/C++) | **补码是现代计算机的标准选择**,因其运算高效、硬件友好。理解补码是处理二进制数据、网络协议、文件解析的基础。 ## 示例 ![image](https://cdn.res.knowhub.vip/c/2505/07/59c6dffe.png?G1IAAMS22TiVdipY29gP%2fkNfQTUDDEkElWrW6713n0b0%2fQGG5mf2seJ8%2bL2PFeTsUALDWJG6cFFDcYglE6leauW8ZwA%3d) 在 Java 中,`byte` 类型是 **有符号的 8 位整数**,**现代计算机(包括 Java)全部使用补码存储有符号整数**,第一位为符号位,取值范围是 `10000000 ~ 01111111`,即:**-128 到 127** (即 `0x80` 到 `0x7F`)。 `0xFC`(二进制 `1111 1100`)到 `byte` 变量时,Java 会将其解释为 **有符号的补码(two's complement)**,因此 `0xFC` 会被视为 `-4`。 * * * ### **为什么 在Java中 `0xFC` 变成 `-4`?** 1. **`byte` 是有符号的**: - `0xFC`(二进制 `1111 1100`)的最高位是 `1`,表示它是一个负数。 - Java 使用 **补码(two's complement)** 表示负数。 2. **补码转十进制**: - 补码的规则:`负数值 = 原码取反 + 1`。 - `0xFC`(`1111 1100`)是某个负数的补码,计算它的原码: ```csharp 补码: 1111 1100 (0xFC) 取反: 1000 0011 +1 : 1000 0100 (结果是 -4) ``` - 因此,`0xFC` 表示 `-4`。 * * * ### **示例代码** ```java byte b = (byte) 0xFC; // 0xFC 被强制转换为 byte 类型 System.out.println(b); // 输出 -4 System.out.println(Integer.toHexString(b & 0xFF)); // 输出 "fc"(正确显示无符号值) ``` * * * ### **如何正确查看 `0xFC`?** 如果你希望在调试或打印时看到 `0xFC` 而不是 `-4`,可以: 1. **用 `& 0xFF` 转为无符号 int**: ```java byte b = (byte) 0xFC; // 0xFC 被强制转换为 byte 类型 int unsignedValue = b & 0xFF; // 0xFC (252) System.out.println(unsignedValue); // 输出 252 System.out.println(Integer.toHexString(unsignedValue)); // 输出 "fc" ``` - `& 0xFF` 的作用是清除高位符号扩展,保留低 8 位。 https://www.cnblogs.com/vipsoft/p/16241685.html 2. **使用 `Byte.toUnsignedInt()`(Java 8+)**: ```java byte b = (byte) 0xFC; // 0xFC 被强制转换为 byte 类型 int unsignedValue = Byte.toUnsignedInt(b); // 252 ``` * * * ### **关键点总结** | 表示方式 | 值(十进制) | 说明 | | --- | --- | --- | | `0xFC`(原始值) | -4 | Java `byte` 是有符号的,`0xFC` 是 `-4` 的补码 | | `b & 0xFF` | 252 | 转为无符号整数,正确显示 `0xFC` | | `Byte.toUnsignedInt(b)` | 252 | Java 8 提供的无符号转换方法 | * * * ### **为什么 Java 的 `byte` 是有符号的?** - Java 的设计遵循了 C/C++ 的传统,`byte`、`short`、`int`、`long` 默认都是有符号的。 - 如果需要无符号操作,可以通过 `& 0xFF` 或 `Byte.toUnsignedInt()` 转换。 * * * ### **适用场景** - 处理二进制协议或文件时,可能需要无符号字节(如 RGB 颜色值、网络协议字段)。 - 调试时若看到负数,记得用 `& 0xFF` 转换查看原始十六进制值。