C 语言数的表示(一)
写文章的目的在于整理 C 语言整数溢出相关的知识。
数据类型与表示方法
C 语言中基本数据类型的表示有整型和浮点型,整型有 short,int,long,还有一个修饰词 unsigned,即无符号。声明一个变量,即向内存申请一段对应数据类型的存储地址,申请的变量类型及分配的内存大小,可以通过 sizeof 函数来得出。输出结果如下列表所示:
Type short has a size of 2 bytes.
Type int has a size of 4 bytes.
Type long has a size of 8 bytes.
Type float has a size of 4 bytes.
Type double has a size of 8 bytes.
Type long long has size of 8 bytes
由此可看到各个不同类型数在系统申请内存的字节数。内存中存储的二进制值为机器数,其通过表示方法对应为十进制的真值。
机器数和真值
机器数和真值,在上面所列出的数据类型中,如 short 类,在内存申请的存储空间为 2 字节,即 16 bit 位。在表示中,高位为符号位,0 为正数,1 为负数。
假定 8 位表示一个数,如数字 7,为正数,转换为二进制为 00000111,如果是 -3,负数首位为 1,转换为二进制为 100000111。
如上 3,-3 为真值,00000111/100000111 为其对应的机器数。
原码、反码、补码
原码
原码为符号位 + 真值的绝对值,如 [+1]原 = 00000001,[-1]原 = 10000001,则 8 位二进制原码取值范围为:[11111111 - 011111111],即 [-127 ~ +127]。
反码
反码:正数的反码为其原码,负数的反码为在其原码的基础上,符号位不变,其余位依次取反。如 [+1]反 = 00000001,[-1]反 = 11111110。
补码
正数的补码为其原码,负数的补码为反码 + 1,如 [+1]补 = 00000001,[-1]补 = 11111111。
采用不同编码方式的原因
计算机中,因保持电路简单的设计,计算机只能执行简单的加法运算,减法运算,可以让参数符号位参与运算,即 + 负数。
如上描述中,计算 1 - 1 = 0,对应的机器数原码操作为:
[00000001]原 + [10000001]原 = [10000010]原 = [-2]真,对应的真值为 -2。即参与负数计算的真值结果不正确。
如使用反码参与减法计算,1 - 1 则对应的机器数反码操作为:
[00000001]反 + [11111110]反 = [11111111]反 = [10000000]原 = [-0]真,让符号位参与运算,得到为 -0 的真值,即反码操作中,存在一个 -0 的问题,-0 是没有意义的,而原码和反码本身都存在 ±0 的表示。
为了解决 ±0 的问题,即补码出现:
1 - 1 = 1 + (-1) → [00000001]原 + [10000001]原 → [00000001]补 + [11111111]补 → [00000000]补 = [00000000]原 = [0]真。
即 [00000000] 表示 0,同时可以使用 [10000000] 表示 [-128]。
即 [-1] + [-127] → [10000001]原 + [11111111]原 → [11111111]补 + [10000001]补 → [10000000]原。即使用 -0 的原码表示 -128 的补码。
实际 -128 没有原码和反码的对应值,使用补码不仅修复了原码和反码中存在 0 和计算的问题,还可以多表示一个数值,8 位原码和反码的表示范围为 [-127 ~ +127],补码的表示范围为 [-128 ~ +127]。
即采用补码计算中,数值的取值范围为 [-2(n-1) ~ 2(n-1) - 1]。(n 为位数)