1.数值数据的表示

为什么采用二进制?
- 二进制只有两种基本状态,两个物理器件就可以表示0和1
- 二进制的编码、技术、运算规则都很简单
- 0和1与逻辑命题的真假对应,方便通过逻辑门电路实现算术运算
数值数据表示的三要素
- 进位记数制(十进制、二进制、十六进制)
- 定浮点表示(解决小数点问题,定点小数:小数点固定居于最高位。定点数的原码、补码等还解决了正负号的问题)
- 如何用二进制编码
原码与补码
首先我们先看原码

显而易见,负数的表示就是最高位取1,但是这样存在以下问题
- 0 的表示不唯一
- 加、减运算方式不统一
- 需额外对符号位进行处理
- 特别当 a<b时,实现 a-b比较困难
所以我们又引入补码的概念(机器数就是补码)
补码=模+该数本身(mod模),该数最高位必须是0
我们就可以得到:123的补码=0111 1011;-123的补码=1000 0101,再多举几个例子,我们发现:
- 正数的补码就是其本身
- 负数的补码等于对应正数补码的"各位取反、末位加1
- 正数补码最高位是0,负数补码最高位是1
以及一些结论:
对于某一确定的模,某数减去小于模的另一数,总可以用该数加上另一数负数的补码来代替;
让我们来举一个例子:

最后结果1 0011 1111,再将其mod2^8(运算器只有有限位,假设为n位,则运算结果只能保留低n位,因此,其模为2^n),得到最终结果0011 1111
接下来是特殊数的补码

整数
整数分为无符号整数和有符号整数,例如8位无符号整数最大值1111 1111为255,有符号整数最大值0111 11111为127
c语言规定:若运算中同时有无符号和带符号整数,则按无符号整数运算
浮点数
规格化数

小数点前只有一位非零数
在计算机中,浮点数按如下格式存储

s位是符号位,正数是0负数是1
注意:规格化尾数的小数点总是1,不写进尾数M中
现在让我们举个例子,计算-12.75
首先12.75=1100.11;转化为科学计数法1.10011*2^3;阶数=3+127=130=1000 0010
尾数=100 1100 0000 0000 0000 0000;符号位为1
现在我们再来做一个题,反推

首先是负数;0111 1101是125,125-127=-2,指数为-2;根据尾数得1.11*2^-2;1.11又是1+0.5+0.25=1.75;所以最终的结果是1.75/4=0.4375
非规格化数
++浮点数范围比定点数大,但数的个数没变多,故数之间更稀疏,且不均匀++
说明浮点数不是能表示范围内的任意数!
当输入数据是一个不可表示数时,机器将其转换为最邻近的可表示数

在浮点数中所能表示的最小正数1*2^-126,尾数不能全为0
为什么是-126?这样可以使得出现比规格化数还小的数时程序也能继续下去

此时阶数-126,而尾数的隐藏数变为0
无穷数
在浮点数中阶数全为1并且尾数全为0则为无穷,如果尾数非零则是NaN
接下来,我们再来看计算机对于除数是0是怎么处理的:
计算机中除数为0的结果是 +/- ∞, 不是溢出异常.
这样可以将X/0>Y可作为有效比较
然而Sqrt (- 4.0) 以及0/0为NaN
零
阶数、尾数全为0,-0,+0表示不同
2.数据的存储
数据的基本宽度
存储器按字节编址,字节是最小可寻址单位 ,一般采用MSB(最高有效字节)
字与字长
字长:指数据通路的宽度
字:度量数据类型的宽度,16位(x86)

容量换算单位是1024,速度则是1000
大端:高地址存低字节
小端:高地址存高字节
譬如0x12345在小端机存储的方式

3.数据的运算
按位运算,逻辑运算与移位运算
按位运算
按位与:& 有零则零,两个都是1才是1,1与任何数字与都是那个数本身
按位或:|,有1就是1,两个都是0才是0
按位取反:~,1变0,0变1
按位异或:^,相同则为1,不同则为0
逻辑运算
&&,||,!,不做过多解释
移位运算
左移:<<,扩大两倍(可能会发生溢出)
右移:>>,缩小二分之一(可能有效数据丢失)
C语言中不区分是逻辑还是算术移位,编译器根据x的类型确定
扩展:短转长 无符号数:0扩展,前面补0;带符号整数:符号扩展,前面补符
截断:长转短 强行将高位丢弃,故可能发生"溢出"(没有规定编译器必须报错)
算术运算

- ALUop:用来决定ALU所执行的处理功能。ALUop的位数k决定了操作的种类
- OF:溢出标志,若A.B同号,但与Sum不同号,则1
- SF:符号标志
- ZF:零标志,sum为0,则为1
- CF:进位/错位标志。当加法时,CF=1,表示加法有进位;减法时,CF=1,表示减法不够减
- Sub:为1时做减法,为0时做加法
- MUX:二路选择器
ZF,SF,CF,OF被称为条件标志,在运算电路中产生,被记录到专门的的寄存器中
重要认识
- 计算机中所有算术运算都基于加法器实现!
- 加法器不知道所运算的是带符号数还是无符号数。
- 加法器不判定对错,总是取低n位作为结果,并生成标志信息
整数加法
无符号加溢出条件:CF=1
带符号加溢出条件:OF=1
整数减法
Unsigned: CF=0时,大于
Signed:OF=SF时,大于
整数乘法
高级语言中两个n位整数相乘得到的结果通常也是一个n位整数
无符号:若Puh=0,则不溢出
带符号:若Psh每位都等于Ps的最高位,则不溢出
编译器在处理变量与常数相乘时,往往以移位、加法和减法的组合运算来代替乘法运算
例如:对于表达式x*20,编译器可以利用20=16+4=24+22,将x*20转换为(x<<4)+(x<<2)
①无乘法指令>② 用ALU实现乘法指令>③用乘法器实现乘法指令
整数除法
对于带符号整数来说,n位整数除以n位整数,除-2^(n-1)/-1= 2^(n-1)会发生溢出外,其余情况(除数为0外)都不会发生溢出
正数商取比自身小的最接近整数,负数商取比自身大的最接近整数
编译器在处理一个变量与一个2的幂次形式的整数相除时,常采用右移运算来实现
注意:带符号负整数(天板):加偏移量(2^k-1),然后再右移k 位 ,低位截断
浮点数加减
首先要对阶,小阶向大阶对齐,还要考虑舍入
若运算结果尾数是0,则需要将阶码也置0
附加位
IEEE754规定: 中间结果须在右边加2个附加位
Guard (保护位):在significand右边的位
Round (舍入位):在保护位右边的位;若没有舍入位,采用就近舍入到偶数
舍入方式:
01:舍;11:入 ;10:(强迫结果为偶数)
因此
- int->float,不会发生溢出,但可能有数据被舍入
- int,float->double,能保留精确值
- double->float,int,可能发生溢出,此外,由于有效位数变少,故可能被舍入
- float,double->int,因为int没有小数部分,所以数据可能会向0方向被截断