数字在计算机中的表示

整数的表示

无符号数的原码

即无符号数的二进制表示。

无符号数原码表示的范围从0到2^n^ -1,即{0,1}^w^ → {0,1,…,2^w^ -1}

补码

定义

最高有效位权重被解释为负权,其他有效位权重为正权,对于向量$\vec{x}=[x_{w-1},x_{w-2},…,x_0]$

函数B2T~w~ (Binary to Two’s-complement)为

$B2T_w = -x_{w-1}2^{w-1}+\sum_{i=0}^{w-2}x_i2^i$

补码快速求相反数的方法及证明

所有位全为1的补码的值为-1

已知 $2^0+2^1+2^2+…+2^n=2^{n+1}-1$

故$-2^{w-1}+\sum_{i=0}^{w-2}x_i2^i=-2^{w-1}+2^{w-1}-1=-1$

推论 快速求一个以补码表示的二进制数的相反数

$\bar{x}$ 为一个二进制数的反码

因为 x 与$\bar{x}$ 相加是一个所有位全为1的补码

故,$x+\bar{x}=-1$

故,$-x=\bar{x}+1$

结论:要快速求一个补码二进制数的相反数,只需求这个二进制数的反码(包括符号位,或者说补码并没有符号位一说),再加一

w位补码能表示的范围

一个w位二进制补码的范围:$[-2^{w-1},2^{w-1}-1]$

以一字节为例,最小的数是 1000 0000 = -128,最大的数是 0111 1111 = 127

最小的负数的相反数是它本身:

$-(-128) = -(1000 0000)=(0111 1111)+1 = (1000 0000)= -128$

符号扩展

对于一个4位补码, 1001 ,其值为 -7,如果将其扩展到8为,应使用符号扩展:

即 1111 1001 其值为 -7

符号扩展不改变补码值的证明

对于正数显然成立,对于负数

w位补码为其值为$2^{w-1}+\sum_{i=0}^{w-2}x_i2^i$

符号扩展k位后,w+k位补码的值为

$-2^{w+k-1}+\sum_{i=w}^{w+k-2}2^i+2^{w-1}+\sum_{i=0}^{w-2}x_i2^i$

$=-2^{w+k-1}+\sum_{i=w}^{w+k-2}2^i+2\times 2^{w-1}-2^{w-1}+\sum_{i=0}^{w-2}x_i2^i$

$=2^{w-1}+\sum_{i=0}^{w-2}x_i2^i$

原码和补码的相互转换

  • 原码转换成补码
    正数原码转补码:正数的补码,与原码相同,例如,10的原码为00001010,补码也是00001010
    负数原码转补码:负数的补码:符号位不变,其余各位按位取反,取反后整体加1
    例如:-10的原码为10001010,符号位不变:1 0001010,其余位按位取反:1 1110101,取反后整体加1:11110101 + 1 = 11110110
  • 补码转换成原码
    正数补码转原码:补码的符号位为0,表示该补码的原码是一个正数,所以补码就是该数的原码,例如:补码为00001010,它的符号位是0,代表它是一个正数的补码,正数的原码就是补码,反正也成立,所以它的原码是00001010
    负数原码转补码:补码的符号位为1,表示该补码的原码是一个负数,所以可以这样求负数的原码,符号位不变,其余各位按位取反,然后再整体加1,例如:补码:11110110,符号位不变:1 1110110,其余位按位取反:1 0001001,取反后整体加1:10001001 + 1 = 10001010

浮点数的表示

浮点数的一般表示

参考:

纯小数补码_百度百科 (baidu.com)

纯小数补码学习_小数的补码_月老吃面包的博客-CSDN博客

浮点数的一般表示 - 简书 (jianshu.com)

一般会用补码表示浮点数的尾数,但网络上关于纯小数补码表示的讲解甚少。上课的时候老师也讲的不是很明白,因此花了好长时间查找资料。(百度还是比Bing能搜出来更有用的东西的!)

浮点数一般表示方法如下

阶符 阶码 数符 尾数

阶码和尾数既可以用原码也可以用补码表示。

补充:有符号定点小数的表示

有符号定点小数有一个符号位,小数点隐含在符号位之后,小数点之后的数叫做尾数。有符号定点小数既可以用原码表示,也可以用补码表示。原码情况下,通常表示为s.010101... 其中s为符号位,010101…为尾数。


至于有符号定点小数的补码表示,首先要介绍补码的原理与作用,才能更好地理解有符号定点小数的补码表示。下面首先讨论有符号整数的补码表示

在介绍补码概念之前,先介绍一下“模”的概念:“模”是指一个计量系统的计数范围,如过去计量粮食用的斗、时钟等。计算机也可以看成一个计量机器,因为计算机的字长是定长的,即存储和处理的位数是有限的,因此它也有一个计量范围,即都存在一个“模”。如:时钟的计量范围是0~11,模=12。假设当前时针指向8点,而准确时间是6点,调整时间可有以下两种拨法:一种是倒拨2小时,即8-2=6;另一种是顺拨10小时,8+10=12+6=6,即8-2=8+10=8+12-2(mod 12).在12为模的系统里,加10和减2效果是一样的,因此凡是减2运算,都可以用加10来代替。若用一般公式可表示为:a-b=a-b+mod=a+mod-b。对“模”而言,2和10互为补数。实际上,以12为模的系统中,11和1,8和4,9和3,7和5,6和6都有这个特性,共同的特点是两者相加等于模。

补码的作用是化减法为加法:对于任何一个有模(mod)的计量系统,都可以化减法为加法。例如,将处在6点的钟表时针向后拨2点(减二),也可以将它向前拨10点(加十)。之所以可以这么做,就是因为-2与10关于12同余。对于一个n为二进制的计量系统,它的模(mod)为 2^n^ 。

补码的原理是数学上的同余关系,若 (a - b) / m能够得到一个整数,或者说 (a - b) 能够被 m 整除,就说 a 与 b关于m同余,记作a≡b(mod m)。对于n 位定点整数(以n = 4为例),负数的原码关于其补码关于2^n^ 同余,例如原码1011 (十进制下为-3),其补码1101 (按照原码的解释方法,其真值为13),它们关于 2^4^ 同余(13-(-3)=16,16可以被2^4^ 整除)。

这也是为什么要取一个负数的补码,需要对它除了符号位以外的数按位取反再加1。(为什么补码的取法是按位取反再加一,以四位二进制数为例,四位二进制数的模为16,即1111 + 0001-0011 表示-3。根据同余的定义,(a-b)/m 是一个整数,我们假设它为1,则有a-b=m ,则有xxxx - (-0011)= 1111 + 0001) 即,xxxx = 1111-0011 + 00011111-0011的作用就是按位取反,因此补码的计算方法为对符号位意外的数按位取反再加一。补码的计算方法也可以简述为进制能表示的最大的数减去原码再加一)


以上的讨论都是基于定点整数,下面我们来讨论有符号定点小数的补码表示


定点纯小数计量系统的模为1,因此对于一个负数-x以及它的补码xc,有xc - (-x) = mod, xc = mod - x = 1 - x ,因此要求一个负小数的补码,可以将这个数在十进制下加一,再转换成二进制原码。例如-7/8,加1为1/8,1/8 转换成二进制得到 1001。

另一种更直接的获得负小数补码的方式是,将除符号位以外的位,按位取反,再加上 “1”。这里的“1”,并不是数值上的1,而是在最低位加1。例如,要求-7/8的补码,首先将其转化为原码表示,即1111 (或写成1.111,有符号定点小数的小数点隐含在符号位之后) 。要求其补码,将处符号位以外的数按位取反,得到1000 (或写成1.000),再在最低为加1,得到1001(或写成1.001)


对于一个负小数的补码 $sd_1d_2d_3…d_i$ 或写成 $s.d_1d_2d_3…d_i$,它的每一个位可以这样解释,符号位解释为 s×-2^0^ (即s×1),数值位解释为 $d_i\times 2^{-i} (i=1,2,3,…) $。

它的真值为 $s\times (-2^0) + \sum_{i=1}^{n}d_i\times2^{-i} $


对于n+1位有符号定点小数(n=8)

原码表示的最大和最小定点小数

最大正数0.1111 1111 :1-2^-n^

最小正数0.0000 0000 : 0

最大负数1.0000 0000 : 0

最小负数1.1111 1111 :-(1-2^-n^ )

补码表示的最大和最小顶点小数

最大正数0.1111 1111 :1-2^-n^

最小正数0.0000 0000 : 0

最大负数1.1111 1111:-(1-2^-n^)

最小负数1.0000 0000 :-1

浮点数的阶码和尾数,既可以使用原码表示,也可以使用补码表示。

例如,浮点数 25.125,其原码的二进制表示为$11001.001 = 1.1001001 × 2^4$

如果用16位来表示这个浮点数,它的存储方式如下(阶码、尾数均为原码表示)

阶符(1bit) 阶码 (5bit) 数符(1bit) 尾数(9bit)
0 0 0100 1 1 0010 0100

为了提高精度,充分利用尾数的每一位,规格化浮点数应运而生。规格化浮点数的特点是尾数位最高有效位应为1。这样可以提高数据表示的精度,避免尾数位的浪费。

例如,对于原码表示的浮点数,1.100为规格化浮点数,因为其尾数位最高位为1。

对于补码表示的浮点数,0.100,它为规格化浮点数,因为其尾数位最高位为1。

但对于用补码表示的负小数,情况则不同。例如1.110它就不是规格化浮点数,其最高位虽然是1,但负数从原码转换成补码中,有一个按位取反的操作,在按位取反时,0变成了1。因此,对于补码1.110其转换成原码为1.010 显然它不是规格化浮点数。综上,对于用补码表示的负小数,其尾数为的最高有效位应该为0。

对上面的规则进行归纳,可得到对于规格化浮点数的如下规则:

当尾数为原码时,最高有效位必须为1

当尾数为补码时,尾数的最高位必须与符号位相反。即对于正数,最高有效位必须为1,对于负数,最高有效位必须为0。因为在负数的补码中,数值位的0相当于原码的1的作用。

获得规格化浮点数的方法:

  • 左规,尾数左移一位,阶码-1
  • 右规,尾数右移一位,阶码+1

例如,浮点数 25.125 = 1.1001001 × 2^4 将其右规后,得到0.11001001 × 2^5

表示为(阶码、尾数均为原码表示)

阶符(1bit) 阶码 (5bit) 数符(1bit) 尾数(9bit)
0 0 0101 0 1 1001 0010

IEEE 754

IEEE 读作 eye-triple-ee

IEEE 754是一种表示浮点数的标准,它以这种格式表示浮点数

$V=(-1)^s\times M\times 2^E$

其中s表示符号位

M,表示尾数;E表示阶码。

在IEEE 754中,有三种浮点数,分别是32位的单精度浮点数 float,和64位双精度浮点数double,以及80位浮点数,这里只介绍float 和double。

float 有1位符号位,8位阶码,23位尾数,

double 有1位符号位,11位阶码,52位尾数。

IEEE 754 有三种浮点数 它们分别是:规格化数,非规格化数和特殊值。

规格化数

规格化数的定义是浮点数的一种表示方式,使得尾数的最高有效位总是1。这样可以省略这一位,增加尾数的精度。规格化数有一个隐含的整数位1,所以尾数的范围是[1, 2)。

在规格化数中,阶码不能全为0,也不能全为1。例如,有4位阶码,那么最小的阶码位0001,最大的阶码为1110。

指数 E的计算方式如下,在规格化数中,E = e - Bias,其中 e 是阶码的值,Bias是偏置值,对于一个k位长度的阶码,Bias的大小等于$2^{k-1}-1$。

M的大小位1 + 尾数表示的值。通过省略前置的1,可以多获得一个位来表示尾数,这个被省略的1也叫做隐含的1。

对于一个32位浮点数

绝对值最小的规格化数

S 阶码 尾数 val
绝对值最小的规格化数 0 0000 0001 0000 0000 $2^{1-127}\times1.0=2^{-126}$
绝对值最大的规格化数 0 1111 1110 1111 1111 1111 1111 1111 111 $2^{254-127}\times1.9999 = 2^{127}\times 1.9999$

非规格化数

根据我从网络上找到的信息,非规格化数是一种特殊的浮点数,它的指数位全为0,尾数位前没有隐含的1。非规格化数用于表示0和非常靠近0的数²³。

非规格化数的阶码全为0,此时 E = 1 - Bias, M = 尾数表示的二进制数

非规格化数的左右有2:

  1. 表示0,规格化数的M处于 1~2之间,无法表示 0,而非规格化数只要令尾数为0,就可以表示0了。注意,非规格化数有+0.0与-0.0。
  2. 非规格化数可以相对均匀地表示十分接近于0的数。
S 阶码 尾数
绝对值最小的非规格化数 0 0000 0000 0000 0000 0000 0000 0000 000 0
绝对值最大的非规格化数 0 0000 0000 1111 1111 1111 1111 1111 111 $2^{1-127}\times0.9999=2^{-126}\times0.9999$

特殊值

关于增长的均匀

对于规格化数,其尾数每增长1,尾数绝对值增长(0.0000 0000 0000 0000 0000 001)B 而当尾数到达1,则会进位。实际的值的大小 = 尾数乘以2^E^ ,随着E的增长,浮点数的值的增长速度也会变快,但E不变时,浮点数的增长就是均匀的。

补充

C 语言中的右移操作

x >> k 右移,分为逻辑右移和算数右移,

逻辑右移在高有效位端补k个0,用于无符号数;

算数右移在最高有效位端补k个最高有效位的值,用于有符号数。

New Bing 关于纯小数的补码表示

image-20230312131442881