大多数机器对整数使用补码编码,而对浮点数使用IEEE标准754编码。在位级上理解这些编码,且理解其算术运算的数学优缺点,对正确使用编程语言避免漏洞来说,非常重要。
1、整数运算
(1). 无符号加法
针对w位无符号数:
溢出判断:
(2). 补码加法(有符号加法)
针对w位补码:溢出判断:
(3). 无符号乘法:
(4). 补码乘法(有符号乘法):先做无符号乘法,再把无符号数转为补码
(5). 乘2的幂次
无符号数与有符号数一致左移位操作,再做截断操作
(6). 除2的幂次
无符号数除2的幂次 = 无符号数右移位 + 向下舍入 + 截断操作
有符号数除2的幂次,如果也按无符号数采取右移位,存在舍入问题。因为被除数为负数时,向下舍入并不合理,例如:-171.25=-172,需向上舍入,例如-171.25 = -171。 采用加偏置进行向上舍入,即x/y向上舍入=(x+y-1)/y向下舍入,即[(x+(1<<k)-1)>>k],再做截断操作。
整数运算小结:
表示数字的有限字长限制了可能的值的取值范围,运算结果可能溢出。整数运算均满足交换律、结合律、分配律。补码表示提供了一种既能表示正数也能表示负数的方法,同时使用了与执行无符号相同的位级实现,针对算术运算,无论无符号数还是有符号数表示形式,都有完全一样或者相似的位级行为。
2、浮点数运算
(1). 浮点数表示:S:符号,M:尾数,E:阶码
浮点数编码分为三种类型:(1)、规格化的值;(2)、非规格化的值;(3)、特殊的值;
(1). 规格化的值
条件:exp即不全为0,也不全为1时:
M = 1(隐式默认值) + Frac(51:0)
E = exp - [(2<<k-1) - 1] k为阶码长度11
规格化的值的数量为:[(2<<k)-2]*[2<<63-k]
(2). 非规格化的值
条件:exp全为0时:
M = Frac(51:0)
E = - [(2<<k-1) - 1]
E != 1-[(2<<k-1)-1] 原因:非规格化的值提供非常接近0.0的浮点数编码,去掉1可以起到从非规格化数值到规格化数值之间的平滑过渡。
(3). 特殊的值
条件exp全为1时:
小数域Frac(51:0)全为0时,V代表无穷大;Frac(51:0)为非0时,V为NaN代表“不是一个数”
由于有限的位数限制,浮点数数量为[(2<<k)-1]*[2<<63-k],浮点数在数轴上的分布规律越靠近0越密集,反之间隔越大,非规格化数值在表示范围内为均匀分布。(存在精度问题),所以浮点数运算只能近似的表示实数运算,IEEE浮点格式定义了四种不同的舍入方式(向偶数舍入、向零舍入、向下舍入、向上舍入)。
(2). 浮点运算
* 浮点运算满足交换律,不满足结合律、分配律。
* 浮点加法满足单调性,乘法满足条件单调性。
* 浮点运算缺乏结合律、分配律可能会存在很严重的问题。
* 将大的浮点数转换成整数是一种很常见的程序错误来源。
* 当浮点数非常接近0.0,从而转换成零时,也会下溢。