C语言 03 原码 反码 补码

原码

计算机中所有的数字都是使用 01 这样的二进制数来进行表示的。

这时如果要存储一个数据,比如十进制的 3,那么就需要使用 2 个二进制位来保存,二进制格式为 11,占用两个位置,称为 2 bit 位。

一般占用 8 个 bit 位表示一个字节(B)2 个字节等于 1 个字,所以一个字表示 16 个 bit 位

数字的直接二进制表示称为原码

虽然原码表示简单,但是原码在做加减法的时候,就会出现问题。

以 8 bit 位为例:

  1 + (-1)
取原码:
  0000 0001
  +
  1000 0001
  =
  0000 1010 
取十进制:
  -2

显然结果应该为 0-2 的结果是错误的。

为了解决这一问题,引入了反码

反码

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

经过上面的定义,再来进行加减法:

  1 + (-1) 
原码:
  0000 0001 
  + 
  1000 0001 
反码: 
  0000 0001 
  + 
  1111 1110 
   =
  1111 1111 
逆反码(取反): 
  1000 0000
十进制:
  -0

这样虽然结果看起来对了。
但如果 1111 1111 代表 -00000 0000 代表 +0
+0-0 都等于 0
一个 0 用两个二进制数表示,既浪费也不合理,所以又引入了补码

补码

正数的补码就是其本身
负数的补码是其原码符号位不变,其余各位取反(得到反码),最后 + 1

再来看上面的运算:

  1 + (-1) 
原码:
  0000 0001 
  + 
  1000 0001 
反码: 
  0000 0001 
  + 
  1111 1110
补码:
  0000 0001
  +
  1111 1111
  =
  1 0000 0000  
省略高位: 
  0000 0000
逆补码(本身):
  0000 0000
逆反码(本身):
  0000 0000
十进制:
  0

这样就得到了预期的 0

可以看出,补码是计算机数字存储和运算的完美解决方案,所以计算机中的数字都是以补码存储

C 语言使用的也是补码。

取值范围

按照上面原码的定义,可以根据位数推出取值范围。

比如现在一共有 8 bit 位来保存数据,为了表示正负,可以让第一个 bit 位专门来保存符号,这样能够表示的数据范围就是:

  • 最小:1111 1111 => -127
  • 最大:0111 1111 => 127

这里就有个疑问了,我们熟知的 char 占一个字节,也就是 8 bit 位,但它的取值范围是 -128 ~ 127

-128 是从何而来呢?

计算机中的数字都是以补码存储,则:

  -128
二进制:
  1 1000 0000
逆补码(-1):
  1 0111 1111
逆反码(取反):
  1 1000 0000
十进制:
  -128

可以看出,补码 -128 的原码也是 -128
由于是用 8 bit 位存储数据,1 1000 0000 所以应该舍弃最高位的 1
由此得到 1000 0000
按照惯性思维,1000 0000 应该为 -0
但前面说了,有了 0 不需要 -0
所以就用 1000 0000 表示 -128

这样运算也是不影响结果的:

  1 + (-128) 
原码:
  0000 0001 
  + 
    1000 0000
    =
    1000 0001
逆补码(-1):
    1000 0000
逆反码(取反):
  1111 1111
十进制:
  -127

再来看看代码的运行结果:

#include <stdio.h>

int main() {
    char c = -128;
    printf("%d", c);
}
-128
#include <stdio.h>

int main() {
    char c = -128;
    printf("%d", c + 1);
}
-127

由此可以看出计算机正是这样运算的。

posted @ 2024-03-23 14:06  天航星  阅读(32)  评论(0编辑  收藏  举报