欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 艺术 > 计算机基础(三):深入解析Java中的原码、反码、补码

计算机基础(三):深入解析Java中的原码、反码、补码

2025/6/21 19:30:48 来源:https://blog.csdn.net/qq_35512802/article/details/148684625  浏览:    关键词:计算机基础(三):深入解析Java中的原码、反码、补码

计算机基础系列文章

计算机基础(一):ASCll、GB2312、GBK、Unicode、UTF-32、UTF-16、UTF-8深度解析

计算机基础(二):轻松理解二进制、八进制、十进制和十六进制

计算机基础(三):深入解析Java中的原码、反码、补码

目录

  • 引言
  • 一、 基础概念:三种编码的诞生背景
    • 1、原码
    • 2、反码
    • 3、补码 🏆 - Java的选择
  • 二、 Java的坚定选择:补码一统天下
    • 1、为什么Java整数表示使用补码?
    • 2、为什么byte的范围是-128到127,而不是-127到127?
  • 三、 眼见为实:Java代码验证补码
  • 总结

引言

  在Java的世界里,我们每天都在与整数打交道:int age = 30;long balance = 1000000L;。但你是否思考过,这些数字在计算机内部的真实形态?理解原码、反码、补码不仅是计算机科学的基础,更是深入Java底层、避免隐蔽bug的关键。本文将带你彻底掌握这些二进制表示法的奥秘及其在Java中的实际应用。


一、 基础概念:三种编码的诞生背景

计算机只能处理二进制(0和1)。如何表示有符号整数(正数、负数)?工程师们设计了三种方案

1、原码

  • 定义:最高位表示符号位(0=正数,1=负数),其余位表示数值的绝对值
  • 示例 (8位byte为例):
    • +5原码:0000 0101 (符号位0,绝对值5)
    • -5原码:1000 0101 (符号位1,绝对值5)
  • 优点:人类直观,理解容易
  • 致命缺陷:
    • 存在两种零+0的原码为00000000-0的原码为10000000( 以一个字节长表示 ),浪费表示范围,逻辑上冗余
    • 加减运算复杂:正数和负数相加,数值部分实际上是相减,符号取决于绝对值大者的符号,硬件不能直接相加符号位和数值位
      • 示例1:(+5) + (-5) = 0,程序直接运算❌
      • 原码的加法不支持符号修正(不能自动让结果的符号与数值正确对应)
            0000 0101  (+5)+ 1000 0101  (-5)-----------------1000 1010  结果为 -10
        
      • 示例2:(-5) + (+3) = -2,程序直接运算❌
            1000 0101  (-5)+ 0000 0011  (+3)-----------------1000 1000  结果为 -8
        
      • 示例3:(-5) + (-5) = -10,程序直接运算❌
      • 原码的加法不支持进位消除(多出的进位被丢弃,不影响结果)
            1000 0101  (-5)+ 1000 0101  (-5)-----------------1 0000 1010  结果为 10(注意:最高位溢出了1位舍弃)
        

2、反码

  • 定义:
    • 正数:反码 = 原码
    • 负数:反码 = 负数原码符号位不变,数值位按位取反,或者更简单点正数原码全部按位取反
  • 示例 (8位byte为例):
    • +5反码:0000 0101 (同原码)
    • --5反码:1111 1010 (-5的原码1000 0101按位取反,符号位不变或者+5原码全部按位取反)
  • 遗留问题:
    • 两种零依然存在+0反码为0000 0000-0反码1111 1111(+0的原码按位取反)
    • 循环进位:计算结果最高位有进位时,需要把进位“回加”到最低位(称为“末位加1”或“循环进位”),硬件实现仍不理想
      • 示例1:(+5) + (-5) = 0,程序直接运算✅
      • 反码的加法在数值上可以看作正确,但从表达角度来看,有点不完美
            0000 0101  (+5)+ 1111 1010  (-5)-----------------1111 1111  结果是反码,符号位不变其他按位取反,原码就是1000 0000也就是-0(负零)
        
      • 示例2:(-5) + (+3) = -2,程序直接运行✅
            1111 1010  (-5)+ 0000 0011  (+3)-----------------1111 1101  结果是反码,原码为1000 0010也就是-2
        
      • 示例3:(-5) + (-5) = -10,程序直接运行❌
      • 反码的加法需要循环进位
            1111 1010  (-5)+ 1111 1010  (-5)-----------------1 1111 0100  结果是反码(注意:最高位溢出了1位舍弃),最高位进位:1(需循环加回最低位)1111 0100+         1-----------------1111 0101 这里还是反码,原码为1000 1010也就是-10
        

3、补码 🏆 - Java的选择

  • 定义:
    • 正数:补码 = 原码
    • 负数:补码 = 反码 + 1
  • 示例 (8位byte为例):
    • +5补码:0000 0101
    • -5补码:+5的原码按位取反获得反码1111 1010,再加1获得补码1111 1011
  • 核心优势 (完美解决前两者问题):
    • 唯一的零:+0补码为0000 0000-0补码是+0的原码全部按位取反再加1得到还是0000 0000,溢出一位舍去
    • 减法变加法:A - B = A + (-B) 直接成立,无需额外判断符号位处理循环进位。硬件只需一套加法电路
      • 示例1:(+5) + (-5) = 0,程序直接运算✅
      • 补码加法支持进位消除(多出的进位被丢弃,不影响结果)
            0000 0101  (+5)+ 1111 1011  (-5)-----------------1 0000 0001  结果是补码,先减1获得反码0000 0000,也就是0
        
      • 示例2:(-5) + (+3) = -2,程序直接运算✅
            1111 1011  (-5)+ 0000 0011  (+3)-----------------1111 1110  结果是补码,先减1获得反码1111 1101,符号位不变其他按位取反获取原码1000 0010,也就是-2
        
      • 示例3:(-5) + (-5) = -10,程序直接运算✅
            1111 1011  (-5)+ 1111 1011  (-5)-----------------1 1111 0110  结果为是补码,先减1获得反码1111 0101,符号位不变其他按位取反获得原码1000 1010,也就是-10
        

二、 Java的坚定选择:补码一统天下

1、为什么Java整数表示使用补码?

  • 补码解决了原码和反码的固有问题(双零复杂运算),简化了CPU硬件设计,提高了运算效率
    1. 最大优势正数和负数的加法、减法可以统一处理(解决痛点:原码需要判断正负)
    2. 补码中只有一个 0(解决痛点:原码和反码有+0和-0,也需要单独判断)
    3. 补码支持进位消除(解决痛点:符号参加运行,多出的进位丢弃,不影响结果,反码需要循环进位)
  • Java的所有整数类型(byte, short, int, long)均使用补码表示!这是现代计算机体系结构的标准

2、为什么byte的范围是-128到127,而不是-127到127?

8 位二进制的表示能力

  • Byte 类型占用 8 位(1 字节)存储空间,共有2^8 = 256种可能的二进制组合
  • 在补码体系中,最高位为符号位(0 正 1 负),剩余 7 位表示数值

补码表示法的规则

  • 正数和零:补码与原码相同,范围是 0000 0000(0)到 0111 1111(127),共 128 个值
  • 负数:补码 = 原码取反 + 1,范围是 1000 0001(-127)到 1111 1111(-1),占 127 个值
  • 关键点:1000 0000 被定义为 -128
二进制(补码)十进制值
1000 0000-128
1000 0001-127
1000 0010-126
...
1111 1110-2
1111 1111-1
0000 00000
0000 00011
0000 00102
...
0111 1110126
0111 1111127

为何不是 -127 到 127?

  • 若范围设为 -127 到 127(含 0),仅能表示 127 + 128 = 255 127 + 128 = 255 127+128=255 个值,无法覆盖全部 256 种组合
  • 补码的连续性要求:将 1000 0000 分配给 -128 后:
    • 数值序列形成闭环:127(0111 1111)+1 溢出为 -128(1000 0000),实现连续循环
    • 若不这样设计,会浪费一个二进制组合(1000 0000),且破坏数值连续性,-127(1000 0001)-1正好是-128(1000 0000

编程语言中的实际表现

  • Byte.MAX_VALUE = 127,Byte.MIN_VALUE = -128
  • 赋值超出范围(如 byte b = 128;)会触发编译错误,如127 + 1 = -128(因 0111 1111 + 1 = 1000 0000

三、 眼见为实:Java代码验证补码

  • Integer.toBinaryString(int i) 方法会返回一个整数补码表示的字符串(省略前导零,负数显示完整的32位,int占4个字节
public class ComplementDemo {public static void main(String[] args) {int positive = 5;int negative = -5;// 打印正数5的二进制(补码,省略前导零)System.out.println(Integer.toBinaryString(positive)); // 输出: 101// 打印负数-5的二进制(32位完整补码)System.out.println(Integer.toBinaryString(negative)); // 输出: 1111 1111 1111 1111 1111 1111 1111 1011}
}

解读负数输出:

  • 11111111111111111111111111111011 就是 -5 的 32 位补码
  • 它是由 +5 (00000000_00000000_00000000_00000101) 按位取反 (11111111_11111111_11111111_11111010)
  • 再加 1 得到的 (11111111_11111111_11111111_11111011)

总结

  • 原码:直观但有双零,运算复杂(历史概念)
  • 反码:试图改进运算,仍有双零和循环进位问题(历史概念)
  • 补码 (Java的选择):统一零表示,完美支持 A - B = A + (-B),硬件实现高效简单,是现代计算机整数表示的标准
  • Java实践:byte, short, int, long 均用补码。Integer.toBinaryString() 可查看补码形式

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词