什么是原码、反码和补码

什么是原码、反码和补码

1、机器数

一个数在计算机中的表示形式是二进制的话,这个数其实就叫机器数。

机器数通常是带有符号的(指有正数和负数之分),计算机用最高位存放符号,这个 bit 一般叫做符号位。 正数的符号位为 0, 负数的符号位为 1。比如,十进制中的数 +7 ,计算机字长为8位,转换成二进制就是 0 0 0 0 0 1 1 1(一个 byte 有 8bit,有效的取值范围是 -128 ~ +127)。

如果是 -7 ,就是1 0 0 0 0 1 1 1。一个存储的二进制码分原码、反码、补码,下面我们就来介绍一下什么是原码、反码、补码。

计算机底层使用二进制形式的补码来计算和存储数据

2、原码

十进制数据的二进制表现形式就是原码,原码最左边的一个数字就是符号位,0为正,1为负。

例如:56 => 0 0 1 1 1 0 0 0

左边第一位为符号位,其他位为数据位。

一个 byte 有 8bit,最大值是 0 1 1 1 1 1 1 1(+127),最小值是 1 1 1 1 1 1 1 1(-127)

在计算机中之所以使用二进制来表示原码是因为逻辑简单,对于电路来说只有开或者关两种状态,用二进制是在方便不过的了。如果使用的进制是十进制、八进制或者十六进制的话,电路没有办法表示那么多的状态。

  • 正数计算
    使用原码对正数进行计算不会有任何问题的。

例如:5 + 2

 0 0 0 0 0 1 0 1
+        0 0 1 0
-----------------
 0 0 0 0 0 1 1 1

把这个结果转成十进制刚好就等于 7,完全正确无误。

  • 负数计算

但是如果是负数的话,那计算的结果就会大相径庭了

我们拿 -56 这个数字来举例,它的原码是 1 0 1 1 1 0 0 0,减一之后,就会变成1 0 1 1 0 1 1 1,这个数转成十进制就是 -55。计算前是 -56,减一之后正确的结果应该是 -57(1 0 1 1 1 0 0 1)才对,居然还越减越大了。

 1 0 1 1 1 0 0 0
-              1
-----------------
 1 0 1 1 0 1 1 1

为了解决原码不能用于计算负数的这种问题,这时候,反码它出现了,作为负数的“计算的救星”。

计算规则是正数的反码不变和原码一致,负数的反码会在原码的基础上,高位的符号位不变,其他位取反( 1 变成 0 , 0 变为 1 )。

3、反码

正数的反码是其本身(等于原码),负数的反码是符号位保持不变,其余位取反。反码的存在是为了正确计算负数,因为原码不能用于计算负数。

十进制数字原码反码
+00000 00000000 0000
-01000 00001111 1111
-11000 00011111 1110
-21000 00101111 1101
-31000 00111111 1100
-41000 01001111 1011
-51000 01011111 1010
-61000 01101111 1001
-71000 01111111 1000
  • 负数计算

这时候,我们再来使用反码计算一下 -56 - 1 的结果。

-56 的原码是1 0 1 1 1 0 0 0,如果转成反码(符号位不变,其他位取反)

那么它的反码就是1 1 0 0 0 1 1 1

  1 1 0 0 0 1 1 1
 -              1
-----------------
  1 1 0 0 0 1 1 0

-56 -1 = -57,-57 的原码是1 0 1 1 1 0 0 1,转成反码刚好是1 1 0 0 0 1 1 0,刚好等于刚才我们算出的值。

  • 跨零计算

不过反码也有它的 “ 软肋 ”,如果是负数跨零进行计算的话,计算得出的结果不对.

我们拿 -3 + 5 来举例

-3 的原码是1 0 0 0 0 0 1 1,转成反码的话就是 1 1 1 1 1 1 0 0

 1 1 1 1 1 1 0 0
+        0 1 0 1  
-----------------
 0 0 0 0 0 0 0 1 

把计算结果转成十进制就是 1,这结果显然不对。那么我们该怎么计算呢,这时候,作为反码的补充编码 —— 补码就出现了。

4、补码

正数的补码是其本身,负数的补码等于其反码 +1。因为反码不能解决负数跨零(类似于 -6 + 7)的问题,所以补码出现了。

十进制数字原码反码补码
+00000 00000000 00000000 0000
-01000 00001111 11110000 0000
-11000 00011111 11101111 1111
-21000 00101111 11011111 1110
-31000 00111111 11001111 1101
-41000 01001111 10111111 1100
-51000 01011111 10101111 1011
-61000 01101111 10011111 1010
-71000 01111111 10001111 1001
-1271111 11111000 00001000 0001
-1281000 0000
  • 跨零计算

这时候,我们再来使用反码计算一下 -3 + 5 的结果。

-3 的原码是1 0 0 0 0 0 1 1,转成反码的话就是 1 1 1 1 1 1 0 0,再转成补码就是1 1 1 1 1 1 0 1

 1 1 1 1 1 1 0 1
+        0 1 0 1
----------------- 
 0 0 0 0 0 0 1 0

把这个数转成十进制刚好等于2,结果正确。

5、总结

在计算机当中都是使用补码来进行计算和存储的。补码很好的解决了反码负数不能跨零计算的弊端,并且补码还可以记录一个特殊的值 -128,这个数据在 1 个字节下是没有原码和反码。

学习了原码、反码和补码的知识之后,我们就可以了解到,Java 当中所有的基本数据类型。比如整数类型的数据类型,存储的数都是同样的,区别是在于什么地方,假设存储的值都是 10。

基本数据类型字节数内存中实际存储的值
byte1010000 1010
short1020000 0000 0000 1010
int1040000 0000 0000 0000 0000 0000 0000 1010
long1080000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1010

从上表中我们可以得出一个结论,为了凑齐字节数,所占的字节越大,则前面补的零越多。

类型转换原理

  • 隐式类型转换
public class Test {
	public static void main(String[] args) {
    	// 小的数据类型往大的数据类型进行转换底层就是通过左补零完成的
        byte a = 10; // 0000 1010
        int b = a;	 // 0000 0000 0000 0000 0000 0000 0000 1010
        System.out.println(b);
    }
}
  • 强制类型转换
public class Test {
	public static void main(String[] args) {
    	int a = 300;	   		// 0000 0000 0000 0000 0000 0001 0010 1100
        byte b = (byte) a; 		// 0010 1100
        System.out.println(b);	// 打印出44
        /*
        	int a = 200;	  		// 0000 0000 0000 0000 0000 0000 1100 1000
        	byte b = (byte)a; 		// 1100 1000
        	System.out.println(b);	// 打印出-56
        */
    }
}

补码的运算也适用于逻辑运算符

运算符含义运算规则
&逻辑与0为false,1为true,当都为1时才为true
|逻辑或0为false,1为true,当有至少一个为1时为true,如果都没有则为false
<<左移向左移动,低位补零
>>右移向右移动,高位补零,符号位按照原来数字的符号位不变
>>>无符号右移向右移动,高位补零
<think>嗯,用户这次想系统了解原码反码补码的概念计算方法。从三个引用材料看,内容非常互补:引用1概括了四种机器数的特性应用场景,引用2点明了补码的硬件优势,引用3则详细演示了转换实例运算问题。用户可能正在学习计算机组成原理,需要建立清晰的数值表示知识框架。尤其要注意ta特别询问“计算方法”,说明需要实操指导。观察历史对话,用户偏好结构化呈现,还提到过站内引用规范,说明是严谨的学习者。考虑到原反补码的强关联性,应该用阶梯式结构层层递进:先讲原码的直观性但运算缺陷,再推导出反码作为过渡方案,最后落脚到补码的完善解决。每个环节都要突出“为什么需要这种表示法”,比如用“±0”问题解释反码的局限,用减法电路优化说明补码的硬件价值。特别注意几个关键点:1)8位补码特殊表示-128的规则需要单列说明2)移码虽然用户没提,但引用1/2都强调其阶码用途,应简要补充3)运算示例要选取典型错误案例(如原码算-1+1)正确对比(补码同样算式)。计算规则必须分步呈现:原码直接转二进制→反码取反范围→补码+1的边界处理。用户提供的引用3里-128的转换实例特别典型,要融入示例三。最后用对比表格收尾,方便用户横向比较特性,毕竟引用1/2多次提到运算复杂度差异。安全提示:必须强调这些是计算机内部表示,与数学真值不同。用户给的引用3开头就提到“机器数”概念,这个认知基础不能丢。</think> 好的,这里为你详细解释原码反码补码的概念及计算方法。这些编码方式是计算机内部表示处理有符号整数的核心基础,理解它们对掌握计算机组成原理至关重要。 **核心目的:** 在二进制环境中表示处理**有符号整数**(正数负数),并解决加减法运算的难题。 --- ### **1. 原码(Sign-Magnitude)** * **概念:** 最直观的表示方法。一个数的原码由两部分组成: * **符号位(Sign Bit):** 最高位(最左边的一位)。`0` 表示正数,`1` 表示负数。 * **数值位(Magnitude):** 剩余的其他位。表示该数绝对值的二进制形式。 * **计算方法:** 1. 确定符号位:正数为`0`,负数为`1`。 2. 将数的**绝对值**转换为二进制。 3. 将符号位(`0`或`1`)放在转换好的二进制数的最前面。 * **例子(8位字长):** * `+5` : 绝对值 `5` 的二进制是 `101`,补全位数为 `0000101`。 符号位 `0`。 原码 = `00000101` * `-5` : 绝对值 `5` 的二进制是 `0000101`。 符号位 `1`。 原码 = `10000101` * **特点与问题:** * **直观易懂。** * **存在两个零(双零问题):** `00000000`(+0) `10000000`(-0)。 * **加减运算复杂:** 符号位不能直接参与运算(引用1、3)。需要进行繁琐的判断(同号相加?异号相减?谁大?结果符号?)。 * 例如 `(+1) + (-1)` = `00000001` + `10000001` = `10000010`(-2),结果错误(引用3)。 * **应用受限:** 主要用作浮点数尾码的表示方式(引用1),不适合现代计算机的通用整数加减运算。 --- ### **2. 反码(Ones' Complement)** * **概念:** 为了解决原码运算(尤其是减法)的部分复杂性而引入。规则: * **正数:** 反码 = 原码(与正数的原码相同)。 * **负数:** 在原码的基础上,**符号位保持不变**,**数值位按位取反**(0变1,1变0)。 * **计算方法(负数):** 1. 先写出该负数的**绝对值所对应的二进制**(数值位)。 2. 将数值位按位取反。 3. 最高位(符号位)设为 `1`。 * **例子(8位字长):** * `+5` : 正数,反码 = 原码 = `00000101` * `-5` : 步骤 1. `+5` 的数值位二进制:`0000101`(绝对值) 2. 数值位按位取反:`1111010` 3. 符号位置 `1`: 反码 = `11111010` * **特点与问题:** * 负数的表示方式较原码有变化。 * **仍然存在两个零:** `00000000`(+0) `11111111`(-0)(引用3)。 * **加减运算稍改进:** 可以用加法代替减法 `A - B = A + (-B)`,只需将 `-B` 以反码形式相加。 * 例1:`(+1) + (-1)` = `00000001` + `11111110` = `11111111`(-0),结果在数学上(期望为0)但形式是 `-0`,不理想(引用3)。 * 例2:`(+1) + (-2)` = `00000001` + `11111101` = `11111110`(等于 `-1` 的反码),结果正确(引用3)。 * **结果修正:** 如果最高位有进位(溢出),需要把这个进位加到最低位(循环进位/循环进位加法/End-around Carry)。例如 `A + (-B)` 计算后最高位有进位 `1`,则将其加到最低位 `Result = (Result + 1)`。 * **应用有限:** 因其双零问题运算仍需额外修正,不是最理想方案。 --- ### **3. 补码(Two's Complement)** * **概念:** **现代计算机中表示有符号整数的事实标准**。它彻底解决了反码遗留的双零问题,并完美地将减法转换为加法,无需额外的硬件减法器(引用1、2)。 * **正数:** 补码 = 原码(与正数的原码/反码相同)。 * **负数:** 在**反码的基础上加1**。或等价地:在**原码基础上除符号位外取反后加1**。 * **计算方法(负数):** 1. 方法一(推荐,基于反码): * 先求出该负数的**反码**。 * 在反码的最低位**加1**。(注意二进制加法进位)。 2. 方法二(直接): * 先写出该数绝对值的二进制。 * 将整个二进制串(包括符号位位置)按位取反。 * 对取反后的结果加1。 * **例子(8位字长):** * `+5` : 正数,补码 = `00000101` * `-5` : * 方法一: * 反码是 `11111010` * `11111010 + 1 = 11111011` * 方法二: * 绝对值 `5` = `00000101` * 所有位取反:`11111010` * 加 `1`:`11111010 + 1 = 11111011` * 补码 = `11111011` * `-128`(特殊): * 8位补码最小范围是 `-128`。 * 计算:绝对值 `128` 的二进制为 `10000000`(7位不够表示,故用8位数值位)。 * 取反:`01111111` -> `+1` = `10000000` -> 最高位是`1`是负数。 * 因此,`-128` 的8位补码是 `10000000`。**注意:`-128` 没有对应的8位原码反码表示(引用3)。** * **特点与优点:** * **无双零问题:** `00000000` 唯一表示 `0`。负数的表示范围比正数多一个(如8位范围是 -128 ~ 127)。 * **加减法统一为加法:** **减法 `A - B` 等同于 `A + (-B)`**,其中 `-B` 以补码形式表示。符号位直接参与运算。 * 例1:`(+1) + (-1)` = `00000001` + `11111111` = `(1)00000000`(最高位进位`1`被丢弃),结果为 `00000000`(0),完美(引用3)。 * 例2:`(-5) + 6` = `11111011 + 00000110` = `(1)00000001`(进位丢弃) = `00000001`(1),正确。 * **硬件简化:** CPU只需设置硬件加法器即可完成加减运算(引用2)。这是采用补码的最主要原因。 * **广泛使用:** 现代所有通用CPU都使用补码表示处理整数。 **简单转换总结:** | 数字 | 原码 | 反码 | 补码 | | :--- | :----------- | :----------- | :----------- | | +5 | `00000101` | `00000101` | `00000101` | | -5 | `10000101` | `11111010` | `11111011` | | 0 | `00000000` | `00000000` | `00000000` | | -0 | `10000000` | `11111111` | `00000000` | | -128 | N/A | N/A | `10000000` | --- ### **为什么需要补码?核心总结** 1. **解决原/反码的双零问题:** 补码中 `0` 只有唯一表示。 2. **简化运算,统一减法为加法:** `A - B = A + (-B)` 在补码体系中直接运算即得正确结果,符号位参与计算无歧义。 3. **硬件成本最低:** CPU只需一个加法器求补(`取反+1`)电路即可高效完成整数加减法(引用1、2)。 **额外补充:移码(Excess-K)** * **用途:** 主要用于浮点数的**阶码**部分。 * **计算方法:** 给真值 `X` **加上一个固定偏移值 `K`**(通常 `K=2^(n-1)` 或 `2^(n-1)-1`,n为位数),再将转换为无符号二进制数。相当于把整个数轴上移`K`个单位。 * **特点:** 移码的大小关系与真值的大小关系保持一致。这使得比较两个浮点数阶码的大小变得非常简单,直接比较其移码的无符号值即可(引用2)。 * **例子(4位移码,偏置量 K=8 / 2³)**:`+3`的移码 = `3+8=11` -> `1011`;`-3`的移码 = `-3+8=5` -> `0101`。比较 `1011 > 0101`(11>5),对应真值 `+3 > -3`。 ---
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值