原码、反码、补码、移码、浮点数

基本概念及表示范围

定义

  1. 真值:对于无符号数,真值即对应其十进制数值,对于有符号数,首位为符号位首位为1表示负,首位为0表示正,剩余位为对应十进制绝对值
  2. 原码:符号位加上真值的绝对值。即用第一位表示符号 其余位表示值。比如如果是8位二进制: +1 = 1000_0001
  3. 反码:正数的反码是其本身,负数的反码是在其原码的基础上符号位不变,其余各个位取反。
  4. 补码:正数的补码就是其本身,负数的补码是在其反码的基础上+1
  5. 移码:真值+偏置值,n位二进制数偏置值为$2^n - 1$,其简单计算方法为补码符号位取反,数值位不变

浮点数

$V = (-1)^S * M * R^E$,如8.345 * 10^0:

  • S:符号位,取值 0 或 1,决定一个数字的符号,0 表示正,1 表示负
  • M:尾数,用小数表示,例如前面所看到的 8.345 * 10^0,8.345 就是尾数
  • R:基数,表示十进制数 R 就是 10,表示二进制数 R 就是 2
  • E:指数,用整数表示,例如前面看到的 10^-1,-1 即是指数

例如32位二进制浮点数及可表示为下列格式:

20230914131854
20230914131854

IEEE754标准

  • 单精度浮点数 float:32 位,符号位 S 占 1 bit,指数 E 占 8 bit,尾数 M 占 23 bit
  • 双精度浮点数 float:64 位,符号位 S 占 1 bit,指数 E 占 11 bit,尾数 M 占 52 bit

为了使其表示的数字范围、精度最大化,浮点数标准还对指数和尾数进行了规定:

  1. 尾数 M 的第一位总是 1(因为 1 <= M < 2),因此这个 1 可以省略不写,它是个隐藏位,这样单精度 23 位尾数可以表示了 24 位有效数字,双精度 52 位尾数可以表示 53 位有效数字
  2. 指数 E 是个无符号整数,表示 float 时,一共占 8 bit,所以它的取值范围为 0 ~ 255。但因为指数可以是负的,所以规定在存入 E 时在它原本的值加上一个中间数 127,这样 E 的取值范围为 -127 ~ 128。表示 double 时,一共占 11 bit,存入 E 时加上中间数 1023,这样取值范围为 -1023 ~ 1024。

除了规定尾数和指数位,还做了以下规定:

  • 指数 E 非全 0 且非全 1:规格化数字,按上面的规则正常计算
  • 指数 E 全 0,尾数非 0:非规格化数,尾数隐藏位不再是 1,而是 0(M = 0.xxxxx),这样可以表示 0 和很小的数
  • 指数 E 全 1,尾数全 0:正无穷大/负无穷大(正负取决于 S 符号位)
  • 指数 E 全 1,尾数非 0:NaN(Not a Number)
20230914132505
20230914132505

有了这个统一的浮点数标准,我们再把 25.125 转换为标准的 float 浮点数:

  1. 整数部分:25(D) = 11001(B)
  2. 小数部分:0.125(D) = 0.001(B)
  3. 用二进制科学计数法表示:25.125(D) = 11001.001(B) = 1.1001001 * 2^4(B)

所以 S = 0,尾数 M = 1.001001 = 001001(去掉1,隐藏位),指数 E = 4 + 127(中间数) = 135(D) = 10000111(B)。填充到 32 bit 中,如下:

20230914132537
20230914132537

相互转换以及运算

二进制十进制转换

  1. 整数转二进制
    采用”除2取余,逆序排列”法:

    1. 首先用2整除一个十进制整数,得到一个商和余数
    2. 然后再用2去除得到的商,又会得到一个商和余数
    3. 重复操作,一直到商为小于1时为止
    4. 然后将得到的所有余数全部排列起来,再将它反过来(逆序排列),切记一定要反过来!
      假设我们现在需要将42转为二进制,那我们怎么做呢,如下图所示:
      20230914132843
  2. 小数转二进制
    采用”乘2取整,顺序排列”法:

    1. 用2乘十进制小数,可以得到积,将积的整数部分取出
    2. 再用2乘余下的小数部分,又得到一个积,再将积的整数部分取出
    3. 重复操作,直到积中的小数部分为零,此时0或1为二进制的最后一位,或者达到所要求的精度为止

    例如将0.125转换为二进制:

    1. 0.125 * 2 = 0.25 ——0
    2. 0.25 * 2 = 0.5 ——0
    3. 0.5 * 2 = 1.0 ——1
      当小数部分为0就可以停止乘2了,然后正序排序就构成了二进制的小数部分:0.001

    如果小数的整数部分有大于0的整数时,将整数部分和小数部分先单独转为二进制,再合在一起就可以了,

    例如:假设要将8.125 转换为二进制

    1. 现将8转为二进制:得到1000
    2. 再将0.125转为二进制:得到0.001
    3. 合并后为1000.001
  3. 二进制转十进制
    以小数点左边一位为0,往左位阶依次是$2^1,2^2…$,往右是$2^{-1}, 2^{-2}…$,依次相乘并加和即可

原码、反码、补码、移码相互转换

原码 反码 补码 移码
正整数 本身,符号位为0 与原码相同 与原码相同 符号位取反
负整数 本身,符号位为1 符号位不变,数值位按位取反 反码末位加1 符号位取反
正小数 本身,符号位为0 与原码相同 与原码相同 小数无移码
负小数 本身,符号位为1 符号位不变,数值位按位取反 反码末位加1 小数无移码

表示范围

设有一个n位二进制数

表示范围(机器字长为n + 1) 特征
整数原码 $-(2^n -1)\le x \le 2^n - 1$ 0的表示方法有两种,原点对称
小数原码 $-(1 - 2^{-n}) \le x \le 1 - 2^{-n}$ 0的表示方法有两种,原点对称
整数反码 $-(2^n -1)\le x \le 2^n - 1$ 0的表示方法有两种,原点对称
小数反码 $-(1 - 2^{-n}) \le x \le 1 - 2^{-n}$ 0的表示方法有两种,原点对称
整数补码 $- 2^n \le x \le 2^n - 1$ 真值0只有一种形式,并规定1000,0000表示$x = -2^7$
小数补码 $-1 \le x \le 1 - 2^{-n}$ 真值0只有一种格式,规定1.000,0000表示x = -1
整数移码 $- 2^n \le x \le 2^n - 1$ 移码只能用于表示整数,0同样只有一种表示形式,很容易对比大小,移码表示数依次真值递增

在现代计算机中,通常用定点补码表示整数 ,用定点原码表示小数 ,用移码表示浮点数的阶码

加减运算

原码加减:

  • 原码加减比较麻烦,一般采用补码进行运算

补码加减:

  • 直接相加并进位
  • 减法等同于加上负数,
  • 由$[x]{补}$ 求$[-x]{补}$,将所有位取反,末尾加一

乘法运算

  1. 原码:符号位和数值位单独运算
  2. 补码:待补充

除法运算

待补充

符号扩展

  1. 正数(原、反、补码都一样)最高位进行扩展
  2. 负数
    1. 原码:在符号位和数值位之间补0
    2. 反码:在符号位和数值位之间补1
    3. 补码:在符号位和数值位之间补1
  3. 正小数(原、反、补码都一样)末尾进行0扩展
  4. 负小数
    1. 原码:末尾补0
    2. 反码:末位补1
    3. 补码:末位补0

定点数移位运算

算术移位

  1. 原码:符号位不参与移位,左移右移都补0
  2. 反码:符号位不参与移位,左移右移都补1
  3. 补码:符号位不参与移位,左移补0,右移补1

逻辑移位

可看作对无符号数的移位

循环移位

循环补位,注意进位位