AnKate
2493 words
12 minutes
CSAPP 2.3&2.4
2020-09-26

整型数据运算#

无符号加法#

对于两个由ww位二进制数组成的非负数字xxyy0x,y2w10 \leq x,y \leq 2^w - 1

当我们计算这样两个无符号数之和时, 0x+y2w+120\leq x + y \leq 2^{w+1}-2,出现了需要用w+1w+1位二进制数表示的情况。

这时的解决方法是截掉最高位,变回ww位二进制数,相当于在结果数值上减去2w2^w,即如下情况:

x+wuy={x+y,x+y<2wx+y2w,2wx+y<2w+1x +^u_wy = \begin{cases} x + y, & x+y < 2^w\\ x + y - 2^w, &2^w \leq x + y < 2^{w+1} \end{cases}
  • 无符号数加法是否溢出:判断x+yx+y是否小于xxyy,若是,则溢出。

  • 无符号数取反:

    wux={x,x=02wx,x>0-^u_wx = \begin{cases} x, & x=0\\ 2^w - x, & x>0 \end{cases}

补码加法#

与无符号加法原理类似,对于两个ww位二进制数补码组成的的x,yx,y2w1x,y2w11-2^{w-1} \leq x,y \leq 2^{w-1}-1

两个补码之和的范围则有2wx+y2w2-2^w \leq x+y \leq 2^w-2,变成了需要由w+1w+1位二进制数表示的情况。

同样地,截掉最高位,但由于补码最高位具有的性质,导致转换成十进制数值时出现了三种可能性:

x+wty={x+y2w,2w1x+yx+y,2w1x+y<2w1x+y+2w,x+y<2w1x+^t_wy = \begin{cases} x+y-2^w, &2^{w-1}\leq x+y\\ x+y, &-2^{w-1}\leq x+y < 2^{w-1}\\ x+y+2^w, &x+y <-2^{w-1} \end{cases}
  • 判断补码加法的溢出:将x+yx+yx,yx,y分别与00进行比较,x,y<0x,y <0x+y0x+y\ge 0,或x,y>0x,y >0x+y<0x+y < 0时溢出,否则无溢出。

  • 补码取反:

wtx={TMinw,x=TMinwx,x>Tminw-^t_wx= \begin{cases} TMin_w, &x=TMin_w\\ -x, &x>Tmin_w \end{cases}
  • 十六进制数取反:

    首先将十六进制数转化为十进制数,因为取反判断依据为十进制数值与TminwTmin_w的大小关系。判断十六进制的数值是否超出TMaxwTMax_w,若溢出,则转化为二进制数后截掉最高位,后转化为十进制数值;若未溢出,则直接转化为对应数值。

    随后再按照补码取反的规则进行取反。

    最后将十进制数重新转化为十六进制数,正数保持不变;若为负数,首先求该负数的相反数的原码,再取所求原码的补码,最后加上一,所得的二进制数按照无符号数解读,则为最终的十六进制数。

    详情见书P95练习2.23.

无符号乘法#

对于两个由ww位二进制数组成的非负数字xxyy0x,y2w10 \leq x,y \leq 2^w - 1

这样两个数的乘积的范围有0xy(2w1)20\leq x*y\leq(2^w-1)^2,需要用2w2w位的二进制数进行表示。

处理方法是对2w2^w取模,即截断为ww位。

xwuy=(xy)mod2wx*^u_wy = (x*y) \mod {2^w}

补码乘法#

对于两个ww位二进制数补码组成的x,yx,y2w1x,y2w11-2^{w-1} \leq x,y \leq 2^{w-1}-1

这样两个数的乘积的范围有22w2+2w1xy22w2-2^{2w-2}+2^{w-1} \leq x*y \leq 2^{2w-2},需要用2w2w位的二进制数进行表示。

处理方法与无符号乘法类似,截断为ww位后从无符号转化为补码。

xwty=U2Tw((xy)mod2w)x*^t_wy=U2T_w((x*y)\mod2^w)

和常数的乘法#

首先,某个整数xx乘以2k2^k可以等价将xx的二进制数左移kk位,并将该xx按照对应编码的解读方式进行运算(即按照补码或是无符号方法进行解读)。

又已知任意一个十进制数可以由二进制数进行表示,故在乘以其他整数时,可以转化为若干次左移运算的求和:

N=(2a1+2a2++2am)xN=x(2a1+2a2++2am)=(x<<a1)+(x<<a2)++(x<<am)N=(2^{a_1}+2^{a_2}+…+2^{a_m})\\ x*N=x*(2^{a_1}+2^{a_2}+…+2^{a_m})=(x<<a_1)+(x<<a_2)+…+(x<<a_m)

例如:

x11=x(23+21+20)=(x<<3)+(x<<1)+(x<<0)x*11=x*(2^3+2^1+2^0)=(x<<3)+(x<<1)+(x<<0)

除以2的整数次幂#

首先注意,除以2k2^k不完全等同于右移kk位,尽管在无符号数的案例中的效果是相同的,但是在处理补码中的负值的除法时,两者有一些舍入上的微妙差异。

  • 右移kk位的舍入方法总是为向下舍入,即数值为正时去除小数点部分,数值为负时去除小数点部分并且减去一,即:

    x>>k=x/2kx>>k=\lfloor x/2^k\rfloor
  • 补码的负数在除以2k2^k后的舍入情况为向0舍入,即舍入方向均为向0靠近:

    x/2k={x/2k,x0x/2k,x<0x/2^k= \begin{cases} \lfloor x/2^k \rfloor, &x\geq0\\ \lceil x/2^k \rceil, & x<0 \end{cases}

    在处理负值的舍入时,并不能单纯视作右移kk位,需要进行向上舍入的操作。换言之,除法与右移k位的等价关系即为:

    x/2k={x>>k,x0(x+(1<<k)1)>>k,x<0x/2^k= \begin{cases} x>>k,&x\geq0\\ (x+(1<<k)-1)>>k,&x<0 \end{cases}

浮点数#

小数的二进制表示#

对于任意整数bmbm1b2b1b0.b1b2bn+1bnb_mb_{m-1}…b_2b_1b_0.b_{-1}b_{-2}…b_{-n+1}b_{-n},整数部分的二进制数bmbm1b2b1b0b_mb_{m-1}…b_2b_1b_0等同于2ibi\sum2^i * b_i,与之类似地,小数部分的二进制数b1b2bn+1bnb_{-1}b_{-2}…b_{-n+1}b_{-n},代表着2ibi\sum2^i*b_i

与十进制中无法用有限位小数13\frac{1}{3}类似,二进制中只能用有限位小数表示那些能写成x2yx*2^y的数字,而那些不能被表示成这种形式的小数,则需要用类似的循环小数进行表示,如表示15\frac{1}{5}

二进制表示形式对应分数数值对应十进制数值
0.020.0_202\frac{0}{2}0.0100.0_{10}
0.0120.01_214\frac {1}{4}0.25100.25_{10}
0.01020.010_228\frac{2}{8}0.25100.25_{10}
0.001120.0011_2316\frac{3}{16}0.1875100.1875_{10}
0.0011020.00110_2632\frac{6}{32}0.1875100.1875_{10}
0.00110120.001101_21364\frac{13}{64}0.203125100.203125_{10}
0.001101020.0011010_226128\frac{26}{128}0.203125100.203125_{10}
0.0011001120.00110011_251256\frac{51}{256}0.19921875100.19921875_{10}

IEEE浮点数表示规则#

与十进制数的科学计数法类似,在二进制中,可以将V以2为基数进行科学计数法的表示:

V=(1)sM2EV=(-1)^s*M*2^E

在公式中:

  • SS是符号位,在二进制表示中占第一位,S=0S=0时代表正数,s=1s=-1时代表负数。在二进制表示中占首位。
  • MM是尾数,在3232位的二进制表示中占2323位,6464位的二进制表示中占5252位。由frac=fn1f1f0frac=f_{n-1}…f_1f_0表示,但并不完全等同于MM。在不同的情况下取值范围有(0,1ϵ](0,1-\epsilon][1,2ϵ)[1,2-\epsilon)两种情况。
  • EE为指数,exp=ek1e1e0exp=e_{k-1}…e_1e_0EE的编码,在3232位中占88位,6464位中占1111位,但expexp不等同于指数,需要通过偏置操作后才能转化为EE

在存储方式上,尾数MM排在expexp的后面。目的是为了便于进行比较浮点数的大小,通过直接比较指数的不同便能较快的得出大小关系。

浮点数分为三种。

Normalized Values#

这一类的浮点数的expexp不全为1或不全为0,而E=expBiasE=exp-Bias,其中的Bias=2k11Bias=2^{k-1}-1,为一定值。

规定M=1+fracM[1,2)M=1+frac,M\in[1,2)。由于保留的整数部分只能为1,故可以省略整数部分的1位,用于增加小数点部分的一位精度。

Denormalized Values#

这一类的浮点数的exp=0exp=0,规定此时E=1BiasM=fE=1-Bias,M=f,与前一类有所差别。

这一类浮点数的定义有两类用途,当fracfrac部分全部取0时,可以用来表示0.00.0,特别地,S=1S=1,其余为全取0时,能够获得0.0-0.0,这两者在有些方面有所差别,但在其他方面类似(书上未展开说明);fracfrac部分不全为0时,还能用来表示非常接近0.00.0的数。

Special Values#

第三类浮点数的expexp每一位都取1,用于表示一些特殊定义。

SSfracfrac全部取0时,可以用来表示\inftyS=1S=1fracfrac全部取0时,用于表示-\infty。特别地,1.0/0.0=1.0/0.0=1.0/0.0=\infty,1.0/-0.0=-\infty

fracfrac不全为0时,代表NaNNaN,在一些没有定义的计算中会输出这一结果,如1\sqrt{-1}、\infty-\infty

浮点数的舍入#

浮点数的舍入通常情况下为向偶舍入,在该数小于中值时向下舍入,大于中值时向上舍入,等于中值时向最近的偶数舍入。

二进制数字的舍入情况(保留一位小数):

舍入前舍入后舍入情况(向偶舍入)
10.010210.010_210.0210.0_2向下舍入
10.011210.011_210.1210.1_2向上舍入
10.110210.110_211.0211.0_2向上舍入
11.001211.001_211.0211.0_2向下舍入

简言之,保留位数的后面若为10010…0(最高位为1,其余为0)的形式,则需要让保留后的最低有效位数为0;若除去最高位外仍有若干个1,则向上舍入;最高位不为1,则向下舍入。

  • 按照十进制数值转化为IEEE浮点数表达形式的方法:

    首先将十进制小数写成二进制表达式,后表达为二进制的科学计数法,即形如M2EM*2^E的式子。

    根据题目给定的fracfrac部分位数进行判断,若超出给定位数则进行舍入,不超出则保留,最终结果即为fracfrac部分的二进制数。

    根据exp=E+Biasexp=E+Bias,可求出expexp部分的值,按照二进制代码展开,即为expexp部分的二进制数。

    详情见书P122 练习2.52.

浮点数运算#

加法#

对于两个浮点数,其加法为:

(1)sM2E=(1)s1M12E1+(1)s2M22E2(-1)^s*M*2^E=(-1)^{s_1}*M_1*2^{E_1}+(-1)^{s_2}*M_2*2^{E_2}

其中的s,Ms,M需要对齐小数点,再分别做加法,EE即为对齐后的E1E_1

加法性质:

  • 封闭(包括NaNNaN):集合中任意两个元素a,ba,b通过运算ff得出的结果仍在集合中
  • 满足加法交换律
  • 加法结合律不成立
  • 0的性质成立:任意一个数加上0仍然为其本身
  • 相反数存在(\inftyNaNNaN除外)
  • 单调性成立(\inftyNaNNaN除外):aba+cb+ca\geq b \Rightarrow a+c \geq b+c

乘法#

其乘法为:

(1)sM2E=(1)s1M12E1(1)s2M22E2(-1)^s*M*2^E=(-1)^{s_1}*M_1*2^{E_1}*(-1)^{s_2}*M_2*2^{E_2}

其中s=s1+s2M=M1M2E=E1+E2s=s_1+s_2,M=M_1*M_2,E=E_1+E_2

乘法性质:

  • 封闭
  • 乘法交换律成立
  • 乘法结合律不成立
  • 1的性质成立:任意一个书乘以1仍然为其本身
  • 乘法分配律不成立a(b+c)ab+bca*(b+c)\neq a*b+b*c
  • 单调性成立(\inftyNaNNaN除外):abc0acbca\geq b \bigcup c\geq0 \Rightarrow a*c\geq b*c
  • 若运算后的M2M\geq2,则将其右移并且增加EE
  • EE超过范围,则溢出。
  • EE未超过范围,则舍入MM,计算得到fracfrac

注意事项#

运算后若M2M\geq2,则右移并增加EE

EE超过范围,则溢出;若未超过,则舍入MM,并计算得出fracfrac

关于溢出:

  • 若E超过上界,则上溢(Overflow),表示为\infty
  • 若E低于下界,则下溢(Underflow),显示为0

C语言中的浮点数#

C语言中,浮点数的数据类型有floatfloatdoubledouble两种,它们与整型intint的类型转换会改变位表示。

double/floatintdouble/float\rightarrow int:类似于向0舍入(超范围未定义,通常设为TMinTMin

intdoubleint \rightarrow double:由于floatfloat的整数部分与小数部分能够表示的范围更大,故能够精准转换

intfloatint \rightarrow float:数字不会溢出,但是会根据规则舍入

CSAPP 2.3&2.4
https://ankate.github.io/posts/csapp-2324/
Author
AnKate
Published at
2020-09-26