原码、反码、补码


在计算机中, 原码、反码、补码 是表示有符号整数的三种方式,主要用于解决 负数存储和运算 的问题。它们的定义、作用及使用场景如下:

Java 大端、小端存储,short、int 转 byte数组,& 0xFF 清除高位符号扩展,保留低 8 位。


1. 原码(Sign-Magnitude)

定义

  • 最高位表示符号0 为正,1 为负),其余位表示数值。
  • 例如,8 位二进制:
    • +5 的原码:0000 0101
    • -5 的原码:1000 0101

特点

  • 直观,人类容易理解。
  • 问题
    • +0-0 不唯一0000 00001000 0000)。
    • 加减运算复杂,需要额外判断符号位。

使用场景

  • 早期计算机(如 IBM 701)使用原码,但现代计算机基本不再使用。

2. 反码(Ones' Complement)

定义

  • 正数:反码 = 原码。
  • 负数:符号位不变,其余位 按位取反
  • 例如:
    • +5 的反码:0000 0101(同原码)
    • -5 的反码:1111 1010(符号位 1,数值位取反)

特点

  • 解决了 +0-0 的问题(反码下 -01111 1111,但仍有冗余)。
  • 缺点
    • 加减运算仍需处理进位(如 -5 + 5 = 1111 1010 + 0000 0101 = 1111 1111,即 -0)。
    • 硬件实现复杂。

使用场景

  • 早期计算机(如 PDP-1)使用反码,但已被补码取代。

3. 补码(Two's Complement)

定义

  • 正数:补码 = 原码。
  • 负数:反码 + 1(即 取反后加 1)。
  • 例如:
    • +5 的补码:0000 0101(同原码)
    • -5 的补码:
      原码:1000 0101
      反码:1111 1010
      补码:1111 1011 (反码 + 1
      

特点

  • 解决了 +0-0 问题(补码下 -0 表示为 0000 0000,与 +0 相同)。
  • 加减运算统一(直接按二进制加法计算,无需额外处理符号位)。
  • 硬件实现简单(只需加法器,无需额外电路)。

使用场景

  • 现代计算机(包括 Java)全部使用补码存储有符号整数
  • 例如:
    • Java 的 byteshortintlong 均用补码表示。
    • 0xFC1111 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)
      5 的补码:0000 0101
      -3 的补码:1111 1101
      相加:0000 0101 + 1111 1101 = 0000 0010 (即 2
      

(3)硬件优化

  • 补码运算无需额外判断符号位,减少电路复杂度。

5. 实际应用示例

(1)Java 中的 byte 类型

byte b = (byte) 0xFC; // 0xFC = 1111 1100(补码)
System.out.println(b); // 输出 -4(因为补码 1111 1100 表示 -4)

(2)无符号转换

由于 Java 没有无符号 byte,需用 & 0xFF 转换:

int unsignedValue = b & 0xFF; // 0xFC(252)
System.out.println(unsignedValue); // 输出 252

(3)网络协议 & 文件解析

  • 读取 TCP/IP 报文、二进制文件时,数据可能是补码形式,需按补码解析:
    // 从文件读取 2 字节的 short(补码存储)
    short value = (short) ((bytes[0] << 8) | (bytes[1] & 0xFF));
    

6. 总结

表示方式定义优点缺点使用场景
原码符号位 + 绝对值直观±0 问题,运算复杂早期计算机
反码负数:符号位 + 取反解决 ±0 问题运算仍有进位问题过渡方案(如 PDP-1)
补码负数:反码 + 1统一加减法,硬件简单现代计算机(Java/C/C++)

补码是现代计算机的标准选择,因其运算高效、硬件友好。理解补码是处理二进制数据、网络协议、文件解析的基础。

示例

image

在 Java 中,byte 类型是 有符号的 8 位整数现代计算机(包括 Java)全部使用补码存储有符号整数,第一位为符号位,取值范围是 10000000 ~ 01111111,即:-128 到 127 (即 0x800x7F)。
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
    • 0xFC1111 1100)是某个负数的补码,计算它的原码:
      补码: 1111 1100 (0xFC)
      取反: 1000 0011
      +1  : 1000 0100 (结果是 -4)
      
    • 因此,0xFC 表示 -4

示例代码

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

    byte b = (byte) 0xFC; // 0xFC 被强制转换为 byte 类型
    int unsignedValue = b & 0xFF; // 0xFC (252)
    System.out.println(unsignedValue); // 输出 252
    System.out.println(Integer.toHexString(unsignedValue)); // 输出 "fc"
    
  2. 使用 Byte.toUnsignedInt()(Java 8+)

    byte b = (byte) 0xFC; // 0xFC 被强制转换为 byte 类型
    int unsignedValue = Byte.toUnsignedInt(b); // 252
    

关键点总结

表示方式值(十进制)说明
0xFC(原始值)-4Java byte 是有符号的,0xFC-4 的补码
b & 0xFF252转为无符号整数,正确显示 0xFC
Byte.toUnsignedInt(b)252Java 8 提供的无符号转换方法

为什么 Java 的 byte 是有符号的?

  • Java 的设计遵循了 C/C++ 的传统,byteshortintlong 默认都是有符号的。
  • 如果需要无符号操作,可以通过 & 0xFFByte.toUnsignedInt() 转换。

适用场景

  • 处理二进制协议或文件时,可能需要无符号字节(如 RGB 颜色值、网络协议字段)。
  • 调试时若看到负数,记得用 & 0xFF 转换查看原始十六进制值。
原创作者: vipsoft 转载于: https://www.cnblogs.com/vipsoft/p/18855000
### 原码反码补码的概念及区别 在计算机系统中,数值的表示和运算依赖于原码反码补码这三种编码形式。它们的核心区别在于对负数的表示方式不同,并且在加减法运算中的处理逻辑也有所差异。 #### 原码 原码是最直观的二进制表示方法,其中最高位为符号位(0 表示正数,1 表示负数),其余位表示数值的绝对值。例如: - +1 的 8 位原码为 `00000001` - -1 的 8 位原码为 `10000001` 原码的优点是表示直观,但缺点是在进行加减运算时需要额外判断符号位,导致计算复杂度较高 [^1]。 #### 反码 反码是对原码的改进形式,主要用于简化补码的生成或解析过程: - 正数的反码原码相同。 - 负数的反码为符号位保持不变,其余位逐位取反(0 变 1,1 变 0)。 例如: - +1 的反码为 `00000001` - -1 的反码为 `11111110` 需要注意的是,在 8 位系统中,+0 和 -0 的反码分别为 `00000000` 和 `11111111`,这会导致两个不同的编码表示同一个数值 [^1]。 #### 补码 补码是现代计算机中最常用的数值表示方式,它解决了原码反码中存在的多个问题,尤其是简化了加减法运算的实现: - 正数的补码原码相同。 - 负数的补码反码加 1。 例如: - +1 的补码为 `00000001` - -1 的补码为 `11111111` 补码的一个重要特性是其可以表示一个比原码范围更广的数值。例如,在 8 位系统中,原码的表示范围为 -127 到 +127,而补码的表示范围为 -128 到 +127。其中 `-128` 的补码为 `10000000`,这个值没有对应的原码表示 [^2]。 #### 计算方法总结 | 类型 | 正数 | 负数 | |--------|--------------------------|--------------------------------------| | 原码 | 符号位为 0,其余为数值本身 | 符号位为 1,其余为数值的绝对值 | | 反码 | 与原码相同 | 原码符号位不变,其余位取反 | | 补码 | 与原码相同 | 反码加 1 | #### 在计算机底层的应用 补码被广泛用于计算机的底层数值存储和运算,主要原因如下: 1. **统一加减运算**:使用补码可以将减法转换为加法,从而简化硬件设计。 2. **唯一零表示**:在补码系统中,0 的表示是唯一的(全 0)。 3. **溢出处理**:补码支持模运算,因此可以自然地处理溢出情况。 例如,在 Java 中,当整数类型发生截断时,结果会自动以补码形式解释。以下代码展示了如何通过强制类型转换截断高位,得到补码表示的数值: ```java public class Main { public static void main(String[] args) { int a = 300; // 00000000 00000000 00000001 00101100 byte b = (byte)a; // 00101100 -> 44 int c = 200; // 00000000 00000000 00000000 11001000 byte d = (byte)c; // 11001000 -> -56 System.out.println(b); // 输出 44 System.out.println(d); // 输出 -56 } } ``` 上述代码表明,当高位被截断后,低位部分按照补码规则重新解释为有符号整数 [^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值