原码、补码、反码和Java中整型数据的表示

1. 原码、补码、反码和Java中整型数据的表示

Java位运算如果有不太懂得地方请查看这篇博客


1.1 显示32位整数在计算机里面的状态(二进制补码)

代码:

    public static void print(int num) {
        for (int i = 31; i >= 0; i--) {
            /*
             * 1 << i
             * 如果i = 31, 1<<31 = 1000...0000(一共32位), num & 这个数相当于把 num的第32位求出来
             * 如果i = 30, 1<<30 = 0100...0000(一共32位), num & 这个数相当于把 num的第31位求出来
             * num & (1 << i) 相当于把 num的第 i + 1位求出来, 因为 0 & N = 0, 1 << i 除了第 i + 1位为1, 其余位都位都为0
             * 当num第i位为0时,  num & (1 << i) = 0
             * 当num第i位为1时, num & (1 << i) = 1
             */
            System.out.print((num & (1 << i)) == 0 ? "0" : "1");
        }
        System.out.println();
    }

运行结果:

在这里插入图片描述


1.2 Java中32位整数(int)的表示

在Java中 32位的整数的表示范围是 -231 ~ 231 - 1(其中负数占了一半,非负数占了一半),最高位(32位)表示数字的符号,其中0表示整数,1表示负数。

比如:-231 转为10进制为-2147483648,在计算机内部为:

在这里插入图片描述

232转为10进制为2147483647,在计算机内部为:
在这里插入图片描述


1.3 原码、补码、反码

原码:

原码的表示与机器数真值表示的一样,即用第一位表示符号,其余位表示数值,例如的十进制的的正负1,用8位二进制的原码表示如下:

  • 【+1】= 原:0000 0001
  • 【-1】 = 原:1000 0001

反码:

正数的反码是其原码本身。
负数的反码是在其原码的基础上,符号位不变,其余各位取反。

  • 【+1】= 原: 0000 0001 = 反: 0000 0001
  • 【-1】 = 原: 1000 0001 = 反: 1111 1110

补码:

正数的补码是其原码本身。
负数的补码是在其原码的基础上,符号位不变,其余各位取反后加1(即在反码的基础上加1)。

  • 【+1】= 原: 0000 0001 = 反:0000 0001 = 补:0000 0001

  • 【-1】 = 原:1000 0001 = 反:1111 1110 = 补: 1111 1111


1.4 数据在计算机中的存储形式

计算机只存储补码,在原、反、补码中,正数的表示是一模一样的,而负数的表示是不相同的,所以对于负数的补码来说,我们是不能直接用进制转换将其转换为十进制数值的,因为这样是得不到计算机真正存储的十进制数的,所以应该将其转换为原码后,再将转换得到的原码进行进制转换为十进制数。(机器数包含符号位)


1.5 为什么计算机底层选择存储补码

计算机是不能直接做减法运算的,因为普通电脑硬件中没有减法器,不需要额外的增加硬件电路制造难度,减法器无需存在。但可以优雅的转换成减法

数据在内存里以补码的形式存储是为了简化计算机的结构设计,同时也提高了运算速度。

如何计算?1314 - 520可以转换成1314 + (-520),就这么简单。

1314的补码:0000 0101 0010 0010
-520的补码:1111 1101 1111 1000
  相加可得: 0000 0011 0001 1010  (最高位的1直接溢出不用管, 这样就实现了加法和减法都使用加法的逻辑)  

计算机没有减法概念,不代表没有负数的概念。

计算机中的负数为了方便运算,所以计算机才会采用补码(正数的补码和反码就是原码)存储数据。

在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。


1.6 负号号可用取反加一表示(计算机底层负号的原理)

在这里插入图片描述

解释:(注意取反(~)是对补码进行取反,因为计算机存储的是补码)

1314 的补码:0000 0101 0010 0010
1314 的取反:1111 1010 1101 1101
取反+1可以表示为:
1111 1010 1101 1101
0000 0000 0000 0001
-------------------
1111 1010 1101 1110 即为:-1314的补码

-520 的补码:1111 1101 1111 1000
-520 的取反:0000 0010 0000 0111
取反+1可以表示为:
0000 0010 0000 0111
0000 0000 0000 0001
-------------------
0000 0010 0000 1000 即为:520的补码

1.7 分享一个小发现

package chapter01;

class Solution {
    public static void main(String[] args) {
        System.out.println(Integer.MIN_VALUE);
        System.out.println(-Integer.MIN_VALUE);
        System.out.println(~Integer.MIN_VALUE + 1);
        print(Integer.MIN_VALUE);
        System.out.println("=====================");
        System.out.println(Integer.MIN_VALUE + 1);
        System.out.println(~(Integer.MIN_VALUE + 1) + 1);
        print(Integer.MIN_VALUE + 1);
    }

    public static void print(int num) {
        for (int i = 31; i >= 0; i--) {
            /*
             * 1 << i
             * 如果i = 31, 1<<31 = 1000...0000(一共32位), num & 这个数相当于把 num的第32位求出来
             * 如果i = 30, 1<<30 = 0100...0000(一共32位), num & 这个数相当于把 num的第31位求出来
             * num & (1 << i) 相当于把 num的第 i + 1位求出来, 因为 0 & N = 0, 1 << i 除了第 i + 1位为1, 其余位都位都为0
             * 当num第i位为0时,  num & (1 << i) = 0
             * 当num第i位为1时, num & (1 << i) = 1
             */
            System.out.print((num & (1 << i)) == 0 ? "0" : "1");
        }
        System.out.println();
    }
}

上面代码的运行结果如下:

在这里插入图片述

我们来说明原因:

Integer.MIN_VALUE = -2147483648
在计算机内部表示为: 
1000 0000 0000 0000 0000 0000 0000 0000
取反后(~0111 1111 1111 1111 1111 1111 1111 11111可得
 0111 1111 1111 1111 1111 1111 1111 1111
+0000 0000 0000 0000 0000 0000 0000 0001
----------------------------------------
 1000 0000 0000 0000 0000 0000 0000 0000

1.8 关于取反和反码的区别


1.8.1 位取反(~)运算

位取反运算符为~,其运算规则是:只对一个操作数进行运算,将操作数二进制中的 1 改为 0,0 改为 1。

示例

  1 1 0 1 0
--------------
~ 0 0 1 0 1

示例:

在这里插入图片描述


1.8.2 反码

正数的反码是其原码本身。
负数的反码是在其原码的基础上,符号位不变,其余各位取反。

  • 【+1】= 原: 0000 0001 = 反: 0000 0001
  • 【-1】 = 原: 1000 0001 = 反: 1111 1110

1.8.3 区别

位取反(~) 操作是针对于补码(计算机里面存储的是补码)。
反码操作是针对于原码。



### 原码补码反码移码的计算方法 #### 定义与用途 在计算机系统中,为了有效地处理正负数以及简化硬件设计,引入了四种不同的编码方式:原码反码补码移码[^1]。 #### 计算过程 对于8二进制数为例: - **原码** - 正数的原码为其本身的二进制形式;而负数则是在最高设置为`1`作为符号位,其余部分保持绝对值对应的二进制表示。 - **反码** - 对于正数而言,其反码与其原码相同; - 而对于负数来说,则需先保留最左边一符号位不变,再将其余各取反得到的结果称为该数的反码[^2]。 - **补码** - 当涉及的是正值时,它的补码等于自身的原码; - 若是负值的话,在获取到此数的反码之后还需对该结果加上一个最低有效上的`1`形成最终的补码表达形式。值得注意的是,这样做可以使得两个相反数相加后的总恰好为零(不考虑溢出情况),从而方便CPU执行加法运算操作。 - **移码** - 若要获得某个整型数据类型的移码表现形式,只需简单地把相应数值转换成补码后再对其首进行翻转即可完成整个转变流程[^3]。 ```python def get_original_code(value, bits=8): """ 获取给定十进制整数 value 的原码 """ if value >= 0: binary_str = bin(value)[2:].zfill(bits) else: abs_value = abs(value) sign_bit = '1' magnitude_bits = format(abs_value, f'0{bits-1}b') binary_str = sign_bit + magnitude_bits return binary_str def get_complement_one(code_string): """ 将传入字符串 code_string 中每一都反转并返回新的字符串""" complemented_chars = ['1' if char == '0' else '0' for char in code_string] return ''.join(complemented_chars) def add_binary_strings(bin_a, bin_b): """ 实现两个二进制串相加的功能函数 """ max_len = max(len(bin_a), len(bin_b)) # 补齐长度至最大宽度 a_padded = bin_a.zfill(max_len) b_padded = bin_b.zfill(max_len) result = [] carry = 0 for i in range(1, max_len + 1): total = int(a_padded[-i]) + int(b_padded[-i]) + carry if total >= 2: carry = 1 remainder = str(total % 2) else: carry = 0 remainder = str(total) result.append(remainder) if carry != 0: result.append(str(carry)) reversed_result = list(reversed(result)) joined_result = "".join(reversed_result).lstrip('0') or "0" padded_final_result = joined_result.zfill(max_len) return padded_final_result def get_inverse_code(original_code): """ 根据输入参数 original_code 来决定是否需要改变符号位之外的部分,并据此生成对应的一组新字符序列 """ if original_code.startswith('-'): inverse_without_sign = get_complement_one(original_code[1:]) final_inverse = '-' + inverse_without_sign elif original_code.startswith('+'): final_inverse = '+' + original_code[1:] return final_inverse.lstrip('+-') def get_two_s_complement(inverse_code): """ 接收由 get_inverse_code 函数产生的逆向编码 inverse_code 并在此基础上进一步加工以得出目标对象所代表的那个特定版本下的补充形态 """ positive_part = inverse_code.replace('-', '') incremented_value = add_binary_strings(positive_part, '1') if inverse_code.startswith('-'): two_s_comp_with_sign = '-'+incremented_value else: two_s_comp_with_sign = '+'+incremented_value return two_s_comp_with_sign.strip('+') def convert_to_offset_binary(two_s_complement): """ 把接收到的数据项 two_s_complement 进行必要的调整使之成为偏置二进制格式 """ without_sign = two_s_complement.removeprefix('-').removeprefix('+') flipped_first_bit = ('0' if without_sign[0]=='1' else '1')+without_sign[1:] offset_binary_representation = flipped_first_bit return offset_binary_representation value_example_positive = 5 original_pos = get_original_code(value_example_positive) inverse_pos = get_inverse_code(f"+{original_pos}") two_s_pos = get_two_s_complement(f"-{inverse_pos}") print(f"Positive Value Example ({value_example_positive}):") print(f"\tOriginal Code:\t\t {original_pos}\n\tInverse Code:\t\t {-inverse_pos}\n\tTwo's Complement:\t {two_s_pos}") value_example_negative=-9 original_neg=get_original_code(value_example_negative) inverse_neg=get_inverse_code(original_neg) two_s_neg=get_two_s_complement(inverse_neg) offset_binary_neg=convert_to_offset_binary(two_s_neg) print("\nNegative Value Example (-9):") print(f"\tOriginal Code:\t\t {original_neg}\n\tInverse Code:\t\t {inverse_neg}\n\tTwo's Complement:\t {two_s_neg}\n\tOffset Binary (Move Code):\t {offset_binary_neg}") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeJiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值