欢迎光临
我们一直在努力

位运算00:认识原码,反码,补码

 并非所有数字都有原码、反码、补码三种形式**。这三种编码仅适用于**有符号整数**

(如 `int`、`int8_t`),且不同编码的表示范围和规则不同。且补码是唯一实际使用的标准。

– **无符号整数**(如 `unsigned int`)只有原码(即直接二进制表示),没有反码和补码的概念。
– **浮点数**(如 `float`、`double`)使用 **IEEE 754 标准**,与这三种编码无关。

原码、反码、补码的定义与关系

原码是数值的二进制表示形式,最高位为符号位(0表示正数,1表示负数),其余位表示数值的绝对值。
反码是在原码的基础上,正数的反码与原码相同;负数的反码是符号位不变,其余位按位取反。
补码是在反码的基础上加1(正数的补码与原码相同)。补码是现代计算机中表示有符号整数的标准方式,因其解决了原码和反码中“零的表示不唯一”的问题,并简化了算术运算。

#### ** 举例说明 **
1. **原码(Sign-Magnitude)**  
   – **定义**:最高位表示符号(`0`为正,`1`为负),其余位表示数值绝对值。  
   – **示例**:  
     – `+5`:`0 0000101`  
     – `-5`:`1 0000101`  
   – **问题**:存在 `+0`(`0 0000000`)和 `-0`(`1 0000000`),导致运算复杂。

2. **反码(Ones' Complement)**  
   – **定义**:正数的反码与原码相同(直接二进制表示,符号位为 `0`);

        负数的反码是对原码**按位取反**(符号位不变)。  
   – **示例**:  
     – `+5`:`0 0000101`  
     – `-5`:`1 1111010`  
   – **问题**:仍存在 `+0` 和 `-0`,且加减法需处理进位。

   – **反码是计算机历史的过渡方案**,现已被补码取代。  
   – **核心规则**:正数不变,负数按位取反。  
   – **唯一价值**:帮助理解补码的设计逻辑(补码 = 反码 + 1)。 

3. **补码(Two's Complement)**  
   – **定义**:正数与原码相同;负数是反码加 1(符号位参与运算)。  
   – **示例**:  
     – `+5`:`0 0000101`  
     – `-5`:`1 1111011`  
   – **优点**:消除 `-0`,统一加减法,硬件实现简单。

特殊数字的编码表现

零的表示:

  • 原码中,零有两种表示形式:+0(如00000000)和-0(如10000000)。
  • 反码中,零同样有两种形式:+0(00000000)和-0(11111111)。
  • 补码中,零只有一种形式(00000000),-0的补码与+0相同。

三者转换关系

  • 原码 → 反码:正数不变;负数符号位不变,其余位取反。
  • 反码 → 补码:正数不变;负数在反码基础上加1。
  • 原码 → 补码:正数不变;负数符号位不变,其余位取反后加1。
  • 特点与优缺点

    • 原码:直观,但存在+0和-0的歧义,加减运算复杂。
    • 反码:解决了加减运算问题,但仍存在零的歧义。
    • 补码:唯一表示零,加减运算统一,硬件实现简单。

    – **原码和反码的淘汰**:  
      原码和反码因存在 `±0`、运算复杂等问题,**仅在历史早期或特定场景中出现**,现代通用 CPU 已不再使用。

    #### **2. 为什么补码成为标准?**
    |      特性             | **补码**                             | **原码/反码**                   |
    |———————-|————————————|———————————|
    | 零的表示**        | 唯一(`000…0`)               | 存在 `+0` 和 `-0`               |
    | 加减法统一性    | 直接硬件加法器即可处理  | 需额外电路处理符号和进位        |
    | **硬件复杂度**  | 简单高效                           | 复杂                            |
    | **范围对称性**  | 对称(8 位:-128 ~ +127) | 不对称(8 位原码:-127 到 +127) |

    #### **6. 总结**
    – **计算机中所有有符号整数**(如 `int`、`int32_t`)**均使用补码**。  
    – **原码和反码已退出实际应用**,仅作为历史或理论存在。  
    – **浮点数**和**无符号整数**的编码与补码无关。  

    :除非在博物馆维护古董计算机,否则只需掌握补码!

    对补码进行补充理解:

    补码的计算方法

    对于一个n位二进制数,补码的计算步骤如下:

    正数的补码是其本身。例如,8位系统中+5的补码是00000101。

    负数的补码是其绝对值的二进制表示取反(反码)后加1。例如,-5的补码计算过程:

  • 绝对值5的二进制:00000101
  • 取反(反码):11111010
  • 加1:11111011(即-5的补码)
  • -n与补码的关系

    直接对n取负数(-n)并不等同于补码。补码是二进制表示形式,而-n是数学上的负数概念。补码

    的生成需要具体的位数(如8位、16位)和二进制操作。

    // 原码,反码,补码打印查看

    /bit_proc_00_code_underdtand_01.cc

    // 原码,反码,补码打印查看
    // 输入一个整数,查看打印出来的原码,反码,和补码
    #include <iostream>
    #include <bitset>

    using namespace std;

    // 打印原码,反码,补码,(使用位操作)
    void print(int num)
    {
    // 原码
    bitset<32> original;
    if (num >= 0) {
    original = bitset<32>(num);
    } else {
    original = bitset<32>(-num);
    original.set(31, 1); // 设置符号位
    }

    // 反码
    bitset<32> inverse;
    if (num >= 0) {
    inverse = original;
    } else {
    inverse = original;
    for (int i = 0; i < 31; ++i) {
    inverse.flip(i); // 除符号位外取反
    }
    }

    // 补码
    bitset<32> complement;
    if (num >= 0) {
    complement = original;
    } else {
    complement = inverse;
    // 反码加1
    bool carry = true;
    for (int i = 0; i < 32 && carry; ++i) {
    if (complement[i] == 0) {
    complement.set(i, 1);
    carry = false;
    } else {
    complement.set(i, 0);
    }
    }
    }

    cout << "数值: " << num << endl;
    cout << "原码: " << original << endl;
    cout << "反码: " << inverse << endl;
    cout << "补码: " << complement << endl;
    cout << "——————\\n" << endl;
    }

    int main()
    {
    int num = 0;
    cout << "input num : ";
    while (cin >> num) {
    print(num);
    cout << "input num : ";
    }

    return 0;
    }

    打印测试:

    // 原码,反码,补码打印查看

    /bit_proc_00_code_underdtand_02.cc

    // 原码,反码,补码打印查看
    // 输入一个整数,查看打印出来的原码,反码,和补码

    #include <iostream>
    #include <bitset>

    using namespace std;

    // 打印原码,反码,补码,(使用位操作)
    void print(int num)
    {
    // 原码
    bitset<32> original = bitset<32>(abs(num));
    if (num < 0) original.set(31, 1);
    // 反码
    bitset<32> inverse = original;
    if (num < 0) inverse ^= bitset<32>(0x7FFFFFFF);
    // 补码(直接利用无符号转换)
    bitset<32> complement = bitset<32>(static_cast<unsigned int>(num));
    cout << "数值: " << num << endl;
    cout << "原码: " << original << endl;
    cout << "反码: " << inverse << endl;
    cout << "补码: " << complement << endl;
    cout << "——————\\n" << endl;
    }

    int main()
    {
    int num = 0;
    cout << "input num : ";
    while (cin >> num) {
    print(num);
    cout << "input num : ";
    }

    return 0;
    }

    测试

    // 补码打印

    /bit_proc_00_code_underdtand_03.cc

    // 补码打印

    #include <iostream>
    #include <bitset>

    using namespace std;

    // ### 为什么 `n`,`-n` 可以直接表示其补码?
    // #### 1. 计算机存储的本质
    // 在计算机中,所有整数(无论正负)都以补码形式存储。因此:
    // – `n`:直接输出时,`std::bitset<8>(n)` 会自动显示 `n` 的补码表示。
    // – `-n`:计算时,计算机先求 `n` 的补码,再对其取反加 1 得到 `-n` 的补码,最后以补码形式存储结果。

    // #### 2. 补码的对称性
    // 补码的设计使得 `-n` 的补码 = `n` 的补码的“取反加 1”。
    // – 硬件支持:CPU 的指令集直接支持补码的负数运算(如 `NEG` 指令)。
    // – 编译器行为:`-n` 会被编译为对 `n` 的补码取反加 1 的操作。

    // #### 总结
    // – `n` 和 `-n` 的补码表示是计算机硬件的直接行为,无需手动转换。
    // – 补码的数学性质:`-n` 的补码 = `~n + 1`,这是补码定义的核心。
    // – 实际编程中:直接使用 `-n` 即可获得其补码,因为编译器会自动处理补码运算。

    // **关键点**:
    // 补码的统一表示让计算机能直接用硬件指令处理正负数,而程序员无需关心底层细节。

    int main()
    {
    int n = 0;
    cout << "input num : ";
    while (cin >> n) {
    std::cout << n << " 的补码: " << std::bitset<8>(n) << "\\n";
    std::cout << n << " 的补码取反加1: " << std::bitset<8>(~n + 1) << "\\n";
    std::cout << -n << " 的补码: " << std::bitset<8>(-n) << "\\n";
    std::cout << "input num : ";
    }

    return 0;
    }

    测试

    // 获取最低位的1

    /bit_proc_00_code_underdtand_04.cc

    // 获取最低位的1
    #include <iostream>
    #include <bitset>

    void print(int freePos)
    {
    std::cout << " 初始值 freePos : " << std::bitset<8>(freePos) << " (" << freePos << ")\\n";
    std::cout << " -freePos : " << std::bitset<8>(-freePos) << " (" << -freePos << ")\\n";
    // 提取最低位的1
    // int pos = freePos & (-freePos); 是一个位运算技巧,用于提取二进制数中最右边的1。
    // -pos和pos仅在最低位的1及其右边的的位上相同,右边的位会变成0,左边的位全部相反。
    // -freePos` 的补码表示**: 在计算机中,负数以补码存储,`-freePos` 等于 `~freePos + 1`。
    // `freePos & -freePos` 会保留 `freePos` 最低位的 `1`,其他位清零。
    int pos = freePos & (-freePos);
    std::cout << " 最低位的1 : " << std::bitset<8>(pos) << " (" << pos << ")\\n";
    // 移除已提取的1
    freePos -= pos;
    std::cout << "移除后的 freePos : " << std::bitset<8>(freePos) << " (" << freePos << ")\\n";
    std::cout << "——————\\n" << std::endl;
    }

    int main()
    {
    int num = 0;
    std::cout << "输入一个整数: ";
    while (std::cin >> num) {
    print(num);
    std::cout << "输入一个整数: ";
    }

    return 0;
    }

    测试:

    赞(0)
    未经允许不得转载:171主机测评 » 位运算00:认识原码,反码,补码
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址