原码、反码、补码再探

发布时间 2023-12-02 11:28:54作者: 加固文明幻景

原码、反码、补码再探

概述

三个计算机用来表达负数的形式。

  • 原码
    • 通过第一位的 \(0\) 来直接表示正数,\(1\) 来直接表示负数。
    • 然而计算机并不用这种方式。
  • 反码
    • 即把要表示的负数的绝对值对应的二进制全部取反来表示。
    • 坏处是 \(0\) 有两种表达方式,全 \(0\) 和全 \(1\) ,所以也不常用。
  • 补码
    • 即把要表示的负数的绝对值对应的二进制全部取反后再加一来表示。
    • 计算机用这种方式就很方便。

转换

转出

对于 \(-57\)

\(57\) 的二进制:

\(0011\space1001\)

  • 原码
    • \(1011\space1001\)
  • 反码
    • \(1100\space 0110\)
  • 补码
    • \(1100\space 0111\)

转回

  • 原码 \(1011\space 1001\)
    • \(-0011\space 1001 = -57\)
  • 反码 \(1100\space 0110\)
    • 取反加一
    • \(-2^7 + 2^6 + 2^2 + 2^1 = -58\)
    • \(-58 + 1 = -57\)
  • 补码 \(1100\space 0111\)
    • 直接取反
    • \(-2^7 + 2^6 + 2^2 +2^1 + 2^0 = -57\)

计算

计算机计算二进制减法:

\(66-57 = 9\)

\(67 = 0100\space0010\)

\(-57 = 1100\space 0111\)

\[\space\space\space 0100\space0010 \]

\[+1100\space0111 \]

\[\space10000\space1001 \]

这个数字溢出了 \(1\) 位,所以去低位数的 \(8\) 位,得到的答案就是 \(0000\space1001\),也就是十进制下的 \(9\)

补码的设计使得计算机可以化减为加。

但谈论补码时,务必要确定总位数,如 char 类型的 \(1100\space0111\)int 类型的 \(0\dots0\space 1100\space 0111\) 表示的可不是同一个数字。

\(memset\)

到这里就可以解释为什么memset只对于 \(-1\)\(0\) 有一一对应的赋值。

首先 memset 按字节赋值,即对于每个字节赋予你给定的值。

对于 \(0\) ,在补码体系下,每个字节都是 \(0\),所以对应。

对于 \(-1\),补码体系下,对于一个字节 \(1111\space1111\),每个字节都是 \(1111\space1111\),所以对应。

依照原理,memset \(0xff\)\(1111\space 1111\) 自然也可以把数组初始化为 \(-1\)