未来属于那些相信梦想,并愿意为之付诸行动的人。
满满的
定点数的表示与运算
定点数的表示
定点数的运算
移位运算
章节回顾
加减运算
章节回顾
乘法运算
除法运算
C语言中的整数类型及类型转换
数据的存储和排列
浮点数的表示与运算
浮点数的表示
浮点数标准IEEE754
浮点数的运算
算术逻辑单元(ALU)
电路的基本原理,加法器设计
加法器,ALU的改进
无符号数与有符号数
1)无符号数
指整个机器字长的全部二进制位均为数值位,没有符号位,相当于数的绝对值.若机器字长为8位,则数的表示范围为0——2^8-1,即:0-255
注:通常只有无符号整数,而没有无符号小数.
2)有符号数
机器数的定点表示
定点小数
定点小数是纯小数,约定小数点位置在符号位之后,有效数值部分最高位之前.若数据X的形式为X=x0x1x2x3....xn(其中x0为符号位,x1-xn是数值的有效部分,也称尾数,为最高有效位),则在计算机中的表示形式如图:(机器字长为n+1位)
当x0=0,x1-xn均为1时,X为其所能表示的最大正数,真值为1-2^-n;
当x0=1,x1-xn均为1时,X为其(原码)所能表示的最小负数,真值等于-(1-2^-n);
定点整数
定点整数是纯整数,约定小数点位置在有效数值部分最低位之后.若数据X的形式为X=x0x1x2....xn(其中x0为符号位,x1-xn是尾数,xn为最低有效位),则在计算机中的表示形式如图2.5所示(设机器字长n+1位).
当x0=0,x1-xn均为1时,X为其所能表示的最大正数,真值为2^n-1
当x0=1,x1-xn均为1时,X为其(原码)所能表示的最小负数,真值等于-(2^n-1);
原码,反码,补码,移码
原码
用机器数的最高位表示该数的符号,其余的各位表示数的绝对值.原码的定义如下:
注意:真值零的原码表示有正零和负零两种形式,即[+0]原=00000和[-0]=10000。
反码
反码通常用来作为由原码求补码或由补码求原码的中间过渡。
若符号位为0,则反码与原码相同.
若符号位为1, 则数值位全部取反.
补码
正数的补码=原码
负数的补码=反码末位+1(要考虑进位)
移码
补码的基础上将符号位取反。注意:移码只能用于表示整数
注:移码全0时,对应真值的最小值-2^n;移码全1时,对应真值的最大值为2^n-1;
移码保持了数据原有的大小顺序,移码大真值就大,移码小真值就小;
知识回顾
原码的算术移位
原码的算术移位——符号位保持不变,仅对数值位进行移位.
右移:高位补0,低位舍弃.若舍弃的位=0,则相当于/2:若舍弃的位不等于0,则会丢失精度
左移:低位补0,高位舍弃,若舍弃的位=0,则相当于/2,若舍弃的位不等于0,则会出现严重误差
反码的算术移位
反码的算术移位——正数的反码和原码相同,因此对正数反码的移位运算也和原码相同。
右移:高位补0,低位舍弃.
左移:低位补0,高位舍弃
反码的算术移位——负数的反码数值位与原码相反,因此负数反码的移位运算规则如下,
右移:高位补1,低位舍弃
左移:低位补1,高位舍弃
补码的算术移位
补码的算数移位——正数的补码与原码相同,因此对正数补码的移位运算也和原码相同。
右移:高位补0,低位舍弃
左移:低位补0,高位舍弃
补码的算数移位——负数补码=反码末位+1
导致反码最右边几个连续的1都因进位而变为0,直到进位碰到第一个0为止.
规律——负数补码中,最右边的1及其右边同原码.最右边的1的左边同反码.
负数补码的算数移位规则如下:
右移(同反码):高位补1,低位舍弃
左移(同原码):低位补0,高位舍弃
逻辑移位
逻辑移位将操作数视为无符号数,移位规则:逻辑左移时,高位移丢,低位添0;逻辑右移时,低位移丢,高位添0
注意:逻辑移位不管是左移还是右移,都添0
循环移位
循环移位分为带进位标志位CF的循环移位(大循环)和不带进位标志位的循环移位(小循环),过程如图2.7所示
循环移位的主要特点是,移出的数位又被移入数据中,而是否带进位则要看是否将进位标志位加入循环位移。例如.带进位位的循环左移就是数据位连同进位标志位一起左移,数据的最高位移入进位标志位CF,而进位位则依次移入数据的最低位
原码加减运算
这样用减法器和加法器可以求出结果,加法器设计起来简单,不过减法器设计起来就很复杂了而且成本也高。那么我们能不能想一个方法,只用加法器来完成这个过程?
加法代替减法
我们举个例子,假如现在10点,我们如何将其调到7点?第一种方法就是逆时针调三格,10-3就可以了,第二种方法就是顺时针调九个,(10+9)%12,这就相当于求余数了
模运算性质:
带余除法——设x,m属于Z,m>0则存在唯一决定的整数q和r,使得:
x=qm+r 0<=r
说明-3,9,21,33,-15这些数都是等价的
想用加法代替减法。就可以用9代替-3,这两者又是互补的
所以:模-a的绝对值=a的补数(12-3=9)
在机器字长为8bit位上,可以看成是模2^8,数都映射到0-2^8-1上.:
计算机只有八个比特字长,硬件会天然的帮我们模2^8,最高位1会被处理掉,结果就是0了
补码——让减法操作转变为加法操作,节省硬件成本.
这里其实就是求该数的补码(模-a的绝对值=a的补数)
补码加减运算
溢出判断
计算机如何进行溢出判断?
符号扩展
原码的乘法运算
我们小学的时候算两个数相乘是怎么计算的?现在我们先来模拟一下手算乘法:
实际上就是:
0.1011(乘数)=1*2^-1+0*2^-2+1*2^-3+1*2^-4
0.1101(被乘数)=1101*2^-4
0.1101*0.1011=(1101*1*2^-8)+(1101*2^-7)+(1101*2^-6)+(1101*2^-5)
用移位就可以实现
现在我们要考虑如何用机器实现:
- 实际数字有正负号,符号位如何处理?
- 乘积位数扩大一倍,该如何处理?
- 4个位积都要保存下来最后统一相加?
我们带着这几个问题,接着往下面走:
先看一下这个图,后面会用到
首先看一道例题:
ACC全部初始化为0,看MQ的最右边的第一位是1/0,当前位为1则ACC加上被乘数,为0则ACC加上0
丢弃的那一位就不用看了,直接看方格里的,MQ的最右边第一位还是1,则ACC加上被乘数
00110+01101=10011,然后再逻辑右移一位,ACC高位补0
重复上面的过程,MQ的右边第一位是0,则ACC加上0,再逻辑右移一位
MQ的右边是1,则ACC+X;00100+01101=10001,再逻辑右移一位
MQ最右边只剩下一个0了,但这个0不需要参与运算,因为他是乘数的符号位
还需要根据异或的结果来修改这个符号位:这里显示是-1
最后结果就是:1.10001111
到这里ACC为什么叫乘积高位,MQ为什么叫乘积低位相信大家也都理解了
手算模拟
- 乘数的符号位不参与运算,可以省略
- 原码一位乘可以只用单符号位
总结:符号位通过异或确定,数值部分通过被乘数和乘数绝对值的n论加法,移位完成,根据当前乘数中参与运算的位确定(ACC)加什么。若当前运算位=1,则(ACC)+[|x|]原;若=0,则(ACC)+0;每轮加法后ACC,MQ的内容统一逻辑右移
补码的乘法运算
首先来看下原码和补码乘法的区别
那么上面说到补码要借助辅助位,那就让我们来看看补码的运算器是怎么样的
手算模拟
原码的除法运算
原码的除法运算有恢复余数法和不恢复余数法(加减交替法),这里主要介绍加减交替法
手算除法
如何把这种手算的思想,用手算来实现?
跟上面乘法一样,符号位用异或单独处理,数值位取绝对值进行除法计算
恢复余数法: (机器)
计算机很傻,会先默认上商1,如果搞错了再改上商0,并恢复余数
默认上商1,用被除数减去除数来判断是否商1是否正确,
ACC-除数转换成:
ACC+[-|y|]补->ACC:01011+10011=11110
当机器检测出ACC的首位是1,也就是负数,发现错误了,应该商0,需要恢复余数,这也就是这个名字的由来
恢复余数:ACC+[|y|补]->ACC :11110+01101=01011
这时需要ACC和MQ中内容逻辑左移(符合手算除法),低位补0
下面就和前面一样,需要先商1,然后相减判断ACC首位是否为1,为1则进行恢复余数,再进行逻辑左移,不为1,则不需要恢复余数,直接逻辑左移就行了.这里就不一一展开了,直接给出最后结果,大家可以去试试
恢复余数法(手算)
每次我们都商1,然后恢复余数,这样显示是很麻烦的,那我们能不能减少操作,不恢复余数囊?
我们接着往下看:
根据上面的分析,当余数为负数时,我们不必要进行恢复余数,上图就是它从a变成了2*a-b;也就是向左移一位然后加上|除数| 。这样我们就引出了加减交替法:
加减交替法:
补码的除法运算(加减交替法)
补码一位除法的特点是,符号位与数值位一起参加运算,商符自然形成.除法第一步根据被除数和除数的符号决定是做加法还是减法;上商的原则根据余数和除数的符号位共同决定,同号上商"1",异号上商"0";最后一步商恒置"1"。
加减交替法的规则如下:
- 符号位参加运算,除数与被除数均用补码表示,商和余数也用补码表示
- 若被除数与除数同号,则被除数减去除数;若被除数与除数异号,则被除数加上除数
- 若余数与除数同号,则商上1,余数左移一位减去除数;若余数与除数异号,则商上0,余数左移一位加上除数
- 重复执行第3步操作n次
- 若对商的精度没有特殊要求,则一般采用"末位恒置为1"法
有符号和无符号的数的转换
我们来看这一段程序
int main() { short x=-4321; unsigned short y=(unsigned short)x; printf("x=%d,y=%u",x,y); return 0; }
有符号数x是一个负数,而无符号数y的表示范围显然
不包括x的值,读者可以自己猜想一下这段程序的运行结果,再比较下面给出的运行结果
x=-4321,y=61215;
这样看没有一点关系,但将这两个数化为二进制表示时,我们就会发现其中的规律,如表2.5所示
观察可知,将short int 强制转换为unsigned short只改变数值,而两个变量对应的每位都是一样的。通过这个例子就知道,强制类型转换的结果保持位值不变,仅改变了解释这些位的方式
不同字长整数之间的转换
长整型转换为短整型
int main() { int x=165537,u=-34991; short y=(short)x,v=(short)u; printf("x=%d,y=%d\n",x,y); printf("u=%d.v=%d\n",u,v); }
结果:
x=165537,y=-31071
u=-34991,v=30545
十六进制表示为:x,y,u,v:0x000286a1,0x86a1,0xffff7751,0x7751,得出结论:
当大字长变量向小字长变量强制类型转换时,系统把多余的高位字长部分直接截断,低位直接赋值,因此也是一种保持位值得处理方法.
短整型变长整型
这里就不举例子了,直接说结果:
当这个数为负数,就在这个数的符号位和原有的数值位之间添1
当这个数为正数,就在这个数的符号位和原有的数值位之间添0
数据的"大端方式"和"小端方式”存储
通常用最低有效字节(LSB)和最高有效字节(MSB)来分别表示数的低位和高位。例如,在32位计算机中,一个int型变量i的机器数位01 23 45 65H,其最高有效字节MSB=01H,最低有效字节LSB=67H
大端方式:就是将最高有效字节存放在前面;
小端方式:就是将最低有效字节存放在前面;
定点数的局限性
我的财富:3540;2B定点整数short就可以表示
马云的财富:12345679....4B定点整数int就可以表示
如果换一种货币:1人民币=100000000000津巴布韦币用long型也表示不了
定点数表示的范围是很有限的,但我们又不能无限的增加长度
这就引出了浮点数
从科学计数法理解浮点数
普通计数法:+302657264576
科学计数法:+3.026*10^11=+11+3.026
阶码反映浮点数的表示范围及小数点的实际位置,
尾数M的数值部分的位数n反映了浮点数的精度
浮点数的表示
阶码:常用补码或移码表示的定点整数
尾数:常用原码或补码表示的定点小数
类比十进制:+302657264526=+3.026*10^11;
可以记位:+11+3.026
浮点数的真值:r^E*M;阶码的底一般为2
如果采用8比特存取空间:
这样b的最后一个数字就被舍弃掉了,这样表示的精度就降低了,那我们有没有方法让其不损失精度囊?
浮点数尾数的规格化
举个例子:+302657264526
可记为:+11+3.026
也可记为:+14+0.003(尾数的最高位是无效值,造成精度损失)
那么上面的那个b=2^2*(+0.01001)=2^1*(+0.10010)
规格化浮点数:规定尾数的最高数值位必须是一个有效值
左规:当浮点数运算的结果位非规格化时要进行规格化处理,将尾数算数左移一位,阶码减1;
右规:当浮点数运算的结果尾数出现溢出(双符号位为01或10)时,将尾数算数右移一位,阶码加1
例:a=010;00.1100,b=010;00.1000,求a+b
a=2^2*00.1100,b=2^2*00.1000
a+b=2^2*00.1100+2^2*00.1000
=2^2*(00.1100+00.1000)
=2^2*01.0100(发生了溢出)
右规=2^3*00.1010
注意:采用"双符号位",当溢出发生时,可以挽救。更高的符号位是正确的符号位
规格化浮点数的特点
1.用原码表示的尾数进行规格化:
正数为0.1*.....的形式,其最大值表示为0.11...1;最小值表示为0.10...0;
尾数的表示范围为1/2<=M<=(1-2^-n);
负数为1.1*....的形式,其最大值表示为1.10...0;最小值表示为1.11...1;
尾数的表示范围为-(1-2^-n)<=M<=-1/2;
2.用补码表示的尾数进行规格化:
正数为0.1*.....的形式,其最大值表示为0.11...1;最小值表示为0.10...0;
尾数的表示范围为1/2<=M<=(1-2^-n);
负数为1.0*...的形式,其最大值表示为1.01...1;最小值表示为1.00...0;
尾数表示范围:-1<=M<=-(1/2+2^-n);
移码
移码:补码的基础上将符号位取反,注意:移码只能用于表示整数
移码的定义:移码=真值+偏置值
此处8位移码的偏置=128D=1000 000B,即2^n-1
偏置值一般取2^n-1,此时移码=补码符号位取反
真值-127=-1111111B
移码=-1111111+10000000=0000 0001
真值-3=-11B
移码=-11+10000000=0111 1101
偏置值可以取其他值
令偏置值=127D=0111 1111B,即2^n-1 -1;
真值 -128=1000 0000B
移码=-1000 0000+0111 1111=1111 1111
IEEE 754标准
例题:
浮点数加减运算步骤: 9.85211*10^12+9.96007*10^10
1.对阶; 9.85211*10^12+0.0996007*10^12
思考为什么是小阶向大阶靠齐?将9.96化成0.0996而不是9.85转换成985?因为这样计算机内部这样处理很简单,方便对尾数进行处理
2.尾数加减; 9.951707*10^12
3.规格化; 如果尾数加减出现类似0.0099517*10^12时,需要左规;如果尾数加减出现类似99.517107*10^12时,需要右规
4.舍入; 若规定只能保证6为有效尾数,则
9.9517107*10^12->9.95171*10^12 (多余的直接砍掉)
或者,9.9517107*10^12->9.95172*10^12(若砍掉部分非0,则入1)
或者,也可以采用四舍五入的原则,当舍弃位>=5时,高位入1
5.判溢出 若规定阶码不能超过两位,则运算后阶码超出范围,则溢出 如:9.85211*10^99+9.96007*10^99=19.81218*10^99
规格化并用四舍五入的原则保留6位尾数,得1.98122*10^100
阶码超过两位,发生溢出
二进制浮点数的加减运算
浮点数的加减运算——舍入
强制类型转换
最基本的逻辑运算
优先级:与>或:AB+CD,先算与再算或
分配律:A(C+D)=AC+AD
结合律:ABC=A(BC)
结合律:A+B+C=A+(B+C)
这样设计有什么意义嘛?
Eg:实现AC+AD
根据分配律我们还可以这样设计
本质上逻辑表达式是对电路的数学化描述,简化逻辑表达式,就是在简化电路,就是在省钱。
用门电路求偶校验位
一位全加器
串行加法器
并行加法器
根据前面的并行加法器可知,要算ci;就要计算AiBi+(Ai异或Bi)*Ci-1;那有没有方法去优化囊?更快的产生进位囊?
c0是一开始就知道的,这样就会减少计算成本.
并行加法器的优化
这里有点难理解,大家可以看我截的图,去b站看王道的课;
优点:各级进位信号同时形式,不需要重复计算,节省时间;
缺点:一直套娃,电路设计就会越来越复杂
为了避免这个缺点,我们四个为一组就可以了.
进行不断的套娃;
ALU芯片的优化