CSAPP 第二章 信息的表示和处理

目录

前言

信息存储

寻址和字节顺序

1、整数表示如下

无符号整数表示如下

有符号整数表示如下

原码、反码与补码 

有符号数转无符号数

无符号数转有符号数 

较小数据类型转换为较大数据类型 

较大数据类型转换为较小数据类型

2、整数的运算

无符号数加法

有符号数加法(分正溢出、负溢出) 

无符号数减法:(X’为加法逆元,即相反数) 

有符号数的逆元:(X’为加法逆元,即相反数) 

无符号数的乘法运算:

补码(有符号数)的乘法运算: 

除法运算(2的倍数,可整除的情况):

除法运算(2的倍数,不可整除的情况,需要加一个偏置=2^k - 1): 

3、浮点数:

 浮点数的表示(IEEE): 

舍入

不同数据类型转换

前言

         计算机中研究三种最重要的数字表示。无符号(unsigned)编码基于传统的二进制表示法,表示大于或者等于零的数字。补码(two's-complement)编码是表示有符号整数的最常见的方式。浮点数(floating point)编码是表示实数的科学记数法的以2为基数的版本。

        整数的运算满足结合律,但是浮点数由于表示的精度有限,是不可结合的。大多数机器上,表达式(3.14+1e20)-1e20求得的值会是0.0,而3.14+(1e20-1e20)求得的值会是3.14。

信息存储

        尽管“char”是用来存储文本串中的单个字符,但是它也能被用来存储整数值。数据类型short、int和long可以提供各种数据大小。即使是为64位系统编译,数据类型int 通常也只有4个字节。数据类型long一般在32位程序中为4字节,在64位程序中则为8字节。

寻址和字节顺序

        在几乎所有的机器上,多字节对象都被存储为连续的字节序列,对象的地址为所使用字节中最小的地址。例如,假设一个类型为int的变量x的地址为0x100,也就是说,地址表达式&x的值为0x100。那么,(假设数据类型int为32位表示)x的4个字节将被存储在内存的0x100、0x101、 0x102 和0x103位置。

        排列表示一个对象的字节有两个通用的规则。考虑一个w位的整数,其位表示为[Xw-1,…,X0],其中Xw-1是最高有效位,而X0是最低有效位。假设w是8的倍数,这些位就能被分组成为字节,其中最高有效字节包含位[Xw-1,..., Xw-8],而最低有效字节包含位[X,…, X0],其他字节包含中间的位。某些机器选择在内存中按照从最低有效字节到最高有效字节的顺序存储对象,而另一些机器则按照从最高有效字节到最低有效字节的顺序存储。前一种规则是最低有效字节在最前面的方式,称为小端法(little endian)后一种规则一最高有效字节在最前面的方式,称为大端法(big endian)假设变量X的类型为int,位于地址0x100处,它的十六进制值为0x01234567。地址范围0x100~0x103的字节顺序依赖于机器的类型:

1、整数表示如下

无符号整数表示如下

有符号整数表示如下

原码、反码与补码 

原码:正数的符号位用“0”表示,负数的符号位用“1”表示,其余数位表示数值本身。例如:X= +1010110, [X]=01010110Y=-0110101, [Y]=10110101

正数的反码与其原码相同,负数的反码是在原码的基础上保持符号位不变,其余各位按位求反得到的。例如:X=+1010110[X]=[X]=01010110Y=-0110101[Y]=10110101[Y]=11001010

正数的补码与其原码相同负数的补码是在原码的基础上保持符号位不变,其它的数位1变为 00变为1,最后再加1运算。也就是说,负数的补码是它的反码加1。在计算机中,有符号整数常常用补码形式存储。例如X=+1010110[X]=[X]=[X]=01010110Y=-0110101[Y]=10110101[Y]=11001011对于任意一个数它的补码的补码是原码,即 [[X]]=[X]原。

有符号数转无符号数

用T2U来表示有符号数到无符号数的函数映射,当最高位Xw-1等于1时,此时有符号数X表示一个负数,经过转换后,得到的无符号数等于该有符号数加上2^w;当最高位Xw-1等于0时,此时有符号数x表示一个非负数,得到的无符号数与有符号数是相等的。

无符号数转有符号数 

用U2T来表示无符号数到有符号数的函数映射。当最高位等于0时,无符号数可以表示的数值小于有符号数的最大值,此时转换后的数值不变。当最高位等于1时,无符号数可以表示的数值大于有符号数的最大值,在这种情况下,转换后得到有符号数等于该无符号数减去2^w。

为什么要了解有无符号的相互转换。因为在 C 语言中,在执行一个运算时,如果一个运算数是有符号数,另外一个运算数是无符号数,那么C语言会隐式的将有符号数强制转换成无符号数来执行运算。如下图的比较中。

较小数据类型转换为较大数据类型 

先来看一下把无符号数转换成一个更大的数据类型,例如,我们将一个unsigned char类型变量转换成unsigned short类型。变量a8bit位,而变量b16bit位,对于无符号数的转换比较简单,只需要在扩展的数位进行补0即可,我们将这种运算称为零扩展,具体表示如图所示。

再来看一下把有符号数转换成一个更大的数据类型。当有符号数表示非负数时,最高位是0,此时扩展的数位进行补零即可;当有符号数表示负数时,最高位是1,此时扩展的数位需要进行补1

较大数据类型转换为较小数据类型

int类型强制类型转换成short类型时,int类型高16位数据被丢弃,留下低16位的数据,因此截断一个数字,可能会改变它原来的数值。

对于无符号数:将一个w位的无符号数,截断成k位时,丢弃最高的w-k位,截断操作可以对应于取模运算,即除以2k次方之后得到的余数。

对于有符号数:第一步,我们用无符号数的函数映射来解释底层的二进制位,这样一来我们就可以使用与无符号数相同的截断方式,得到最低 K 位;第二步,我们将第一步得到的无符号数转换成有符号数。

2、整数的运算

无符号数加法

对于第二种情况发生了溢出。如下(溢出被丢弃): 

通过如下可以判断运算结果是否产生溢出: 

有符号数加法(分正溢出、负溢出) 

如下例子: 

判断是否发生溢出: 

无符号数减法:(X’为加法逆元,即相反数) 

有符号数的逆元:(X’为加法逆元,即相反数) 

无符号数的乘法运算:

w位的无符号数x和y,二者的乘积可能需要2w位来表示。在C语言中,定义了无符号数乘法所产生的结果是w位,因此运行结果会截取2w位中的低w位。截断采用取模的方式,因此,运行结果等于x与y乘积并对2的w次方取模。

补码(有符号数)的乘法运算: 

计算机的有符号数用补码表示,因此补码乘法就是有符号数乘法。无论是无符号数乘法,还是补码乘法,运算结果的位级表示都是一样的,只不过补码乘法比无符号数乘法多一步,需要将无符号数转换成补码(有符号数)。虽然完整的乘积结果的位级表示可能会不同,但是截断后的位级表示都是相同的。

由于整数乘法比移位和加法的代价要大得多,许多C语言编译器试图以移位、加法和减法的组合来消除很多整数乘以常数的情况。例如,假设一个程序包含表达式x*14。利用14=2^3+2^2+2^1,编译器会将乘法重写为(x<<3)+(x<<2)+(x<<1),将一个乘法替换为三个移位和两个加法。无论x是无符号的还是补码,甚至当乘法会导致溢出时,两个计算都会得到一样的结果。更好的是,编译器还可以利用属性14=2^4-2,将乘法重写为(x<<4)-(x<<1),这时只需要两个移位和一个减法。

除法运算(2的倍数,可整除的情况):

除法运算(2的倍数,不可整除的情况,需要加一个偏置=2^k - 1): 

3、浮点数:

 浮点数的表示(IEEE): 

三个变量:符号s、阶码E和尾数M,下面以单精度浮点数为例。

例如C语言中float类型的变量占4个字节,32个比特位,这32个比特位被划分成3个字段来解释。

其中最高位31位表示符号位s。当s=0时,表示正数;s=1时则表示负数。从第23位到30位,这8个二进制位与阶码的值E是相关的。剩余的23位与尾数M是相关的。

对于64位双精度浮点数,其二进制位与浮点数的关系如图所示。

与单精度浮点数相比,双精度浮点数的符号位也是1位。但是阶码字段的长度为11位,小数字段的长度为52位。 

浮点数的数值可以分为三类:第一类是规格化的值(Normalized Values),第二类是非规格化的值(Denormalized Valued),第三类是特殊值(Special Values)。其中阶码的值决定了这个数是属于其中哪一类。(特殊值:无穷大/无穷小、不是一个数)

对于规格化的值(Normalized Values):

单精度阶码E范围[-126, 127]

尾数M被定义为1 + f,即M = 1.f22f21…f1f0 = 1 + f。

下面看一个例子(8位浮点数):
假定符号位长度为1、阶码字段长度为4、小数字段长度为3。

 (最后一行不属于规格化的值,是特殊值)

对于非规格化的值(Denormalized Valued),阶码字段全0:

 注意解释方法(左)与规格化方法的解释(右)不同。

下面看一个例子(8位浮点数):
假定符号位长度为1、阶码字段长度为4、小数字段长度为3。

对于特殊值(Special Values),阶码字段全1,三种情况:

舍入

对于值x,可能无法用浮点形式来精确的表示,因此我们希望可以找到“最接近的值x’来代替x,这就是舍入操作的任务。一个关键的问题就是在两个可能的值中间确定舍入方向,例如一个数值1.5,想把该数舍入到最接近的整数,舍入结果应该是1还是2呢?IEEE浮点格式定义了四种不同的舍入方式,分别是:向偶数舍入、向零舍入、向下舍入以及向上舍入。

向下舍入和向上舍入的情况比较简单,向下舍入总是朝向小的方向进行舍入,而向上舍入总是朝向大的方向进行舍入。

向零舍入就是把正数进行向下舍入,把负数进行向上舍入。将这种舍入规则映射到数轴上,可以发现舍入是朝向零的方向。向偶数舍入,也被称为向最接近的值进行舍入。

需要注意的是当遇到两个可能结果的中间数值时,舍入结果应该如何计算,向偶数舍入的结果要遵循最低有效数字是偶数的规则,因此1.5的舍入结果究竟是1还是2,取决于1和2哪个数是偶数。 

例如,我们将下面的两个十进制小数精确到百分位。1.2349999 和1.2350001。

由于这两个数并不在1.23和1.24正中间,所以两个数的舍入结果分别为1.23和1.24,并不需要考虑百分位是否是偶数。由于1.235在1.23与1.24中间,这时我们需要考虑百分位是否是偶数的情况,因此舍入结果是1.24。

不同数据类型转换

当int类型转换成float类型时,数字不会发生溢出,但是可能会被舍入。这是由于单精度浮点数的小数字段是23位,可能会出现无法保留精度的情况。从double类型转换成float类型,由于float类型所表示数值的范围更小,所以可能会发生溢出,此外,float 类型的精度相对于double较小,转换后还可能被舍入。将float类型或者double类型的浮点数转换成int类型,一种可能的情况是值会向零舍入,例如1.9将被转换成1,-1.9将被转换成-1;另外一种可能的情况是发生溢出。

(备注:本文图片较多,图中有较多公式,便于展示,直接贴的图,若有不足,请多多评论指教)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值