聊聊计算机构造基础(第九章):定点数值与定点计算的操作
本文讲什么?
在计算机中,小数点并没有用专门的器件去表示,而是按照一种约定的方式,统一存储在寄存器单元中的。算数逻辑运算单元(ALU)是CPU的组成部分,负责算数和逻辑的运算。那么,ALU究竟是如何工作的呢? 这就是本文主要探讨的内容:
- 什么是定点数?
- 定点数的位移、加、减、乘、除运算是如何进行的?
定点数是啥?
从字面意思来理解,“定点数”就是“点”不动的数。那么究竟是什么“点”不动呢?没错,就是“小数点”。 在上一讲我们说道,不论是整数还是小数,都是有小数点的。整数的小数点表示在最后一位数字的后面,而小数的小数点标识在真值的符号位后面。如图所示:
除了定点数,还有一种数叫做“浮点数”,浮点数将在下一讲展开介绍。
定点数的运算
好了,介绍完定点数的基本概念以后,我们展开讲定点数的位移运算和四则运算。定点数的四则运算实际上要比我们想象的复杂的多。 机器并不像人,一眼就知道二二得四,他需要知道2的定点表示形式,然后两个定点数相乘,相乘是有一定的过程的,经过了这个过程,才能得到结果的二进制数,最终输出给我们。我们要做的,就是了解加减乘除究竟经历了什么样子的过程。
定点数的位移运算
不要看移位运算简单,但是它在计算机的运算中的地位是举足轻重的。没有移位运算,也就没有后面的乘除法,乘除法就是在移位运算和加减运算的配合下实现的。 移位运算的规则:虽然正数和负数的移位运算规则不相同,但是相同之处在于移位后正数和负数的符号不变。那么规则就变成了移位运算时,符号位不动,数值位按照如下规则进行移位。
举个例子:
- 将+26的原码、补码和反码分别左移一位 结果是:[26]原 = [26]反 = [26]补 = 0,0011010,根据规则,原码、反码、补码左移一位的结果是:0,010100
- 将-26的原码、反码、补码分别左移一位 [-26]原 = 1,0011010,左移一位:1,0110100 [-26]反 = 1,1100101,左移一位:1,1001011 [-26]补 = 1,1100110,左移一位:1,100110
移位的运算是不是很简单?只需要根据规则来就可以了。重点在下面的几种运算中。
定点数的加法与及减法
定点数的加减运算只需要记住一个原则:加法直接加,减法先变为加法后再计算。 什么意思呢?比如[A+B]补 = [A+B]补,[A-B]补 = [A]补 + [-B]补。 来看两个例子:
- A = -1001,B = -0101,求[A+B]补 [A+B]补 = [A]补 + [B]补,[A]补 = 1,0111,[B]补 = 1,1011,所以最终的结果是:11,0010,但是这并非我们的最终结果,最终结果应该丢掉第一个1,即1,0010.为什么呢?这涉及到一个模2运算的问题,如果不想深究只需要记住,一个数只能有一个符号位不是吗?
- A = -1001,B=-0101.求[A-B]补 [A-B]补 = [A]补 + [-B]补,[A]补=1,0111,[-B]补=0,0101(求法:[-B]补等于[B]补符号位和各位取反,末位加一),这样得到最终的结果,丢弃掉多余的位即可。
溢出的判断:如果计算机的机器字长为4,那么能够表示的真值范围在-8~+7之间,如果两个数相加减,跳出了这个范围,则为溢出。 那么应当如何判断溢出呢? 原则:
- 不论加法还是减法,只要实际参与运算的两个数的符号相同,但是与最终的结果的符号相反,则为溢出。比如我们的第一个例子,两个参与运算的数的符号相同,且和最终结果的符号也相同,则这种情况就不是溢出。
- 最终结果的两位符号位如果相同,则无溢出,如果不同则溢出,还是第一个例子,计算后的结果是11,0010,两位符号位相同,没有溢出。
定点数的乘法
乘法的运算方式形成过程,我推荐大家看看计算机专业的教材,即唐朔飞老师的《计算机组成原理》。本文奔着实用性的角度,不会过度发掘计算方法的推导过程,因为我的解释并不如教材上的好。至于真正的计算,不能说是优于教材,最起码你你能够快速上手计算,如果你做到了,那么我的目的也就达到了。 定点数乘法的计算方式:
- 原码一位乘
- 说明:有A*B,令初始部分积为0,分别取A、B的绝对值A’、B’。乘数为B’,取乘数最后一位,如果是0,则部分积加0;如果是1,则部分积加A’。加法操作执行完成后,部分积右移一位,继续执行上述操作,直到原有乘数的所有位均被用完。最后一步的加法执行完成后,右移一位,形成最终结果。最终结果的正负由二者的符号的异或决定。 单纯的说你可能会有点懵逼,我们来结合一个实际的例子看看。
- 例:A = 0.1101,B = 0.1011,求A*B。 为了方便,我们将上述的计算过程放入表格中进行计算,表格如下:
乘数一栏的上部分数字暂时不用管,仔细看一下运算步骤,和我总结的对比,仔细体会即可。
- 原码两位乘:原码两位乘有运算规则,和一位乘有着类似的地方,但是不尽相同。原码两位乘是用两位乘数的部分来决定新的部分如何形成的运算方式。两个二进制数共有四种形态:00,01,10,11,可以表示不同的移位方式以及加法的方式(下面会看到)。再加上一位标志位C,就能实现更加复杂的操作。如图所示:
有了上面的规则,就能够很轻易的根据原码一位乘做出原码两位乘,计算过程:初始化部分积为0,写入乘数,标志位置为0.判断乘数的后两位以及标志位满足何种关系,调用相应的方法移位并设置标志位,最后的结果向右移动两位,根据x和y的符号的异或确定符号。有必要说明的是,两位乘需预留出3位符号位。给出个例子:
- 例子:设x=0.111111,y=-0.111001,用原码两位乘求出[x*y]原
这样就非常容易理解了。
- 补码乘法:补码一位乘与原码一位乘类似,区别在于,当乘数为正时,补码乘法运算方法与原码一位乘运算方式相同;当乘数为负数时,补码一位乘前半部分同样和原码一位乘相同,不同之处是在运算完成后,需要把最终结果加上[-x]补用于校验,才能得到最终的结果。这部分的推导同样可以参见教科书。下面给出一个例子:
- 例子:[x]补 = 0.1101,[y]补 = 1.0101,求[x*y]补。由于[y]补为负数则需要在最终的结果加上[-x]补用于校验。
- 补码比较法:相信有了前面的基础,直接给出规则你就能够解决问题了,补码比较法的运算规则如下:
给出例子:[x]补 = 0.1101,[y]补 = 0.1011.运算过程如下:
- 补码两位乘:补码两位乘和上面的方式没有太多的区别,主要就是两位乘采用三位符号位,三位判断位(乘数的位),有了运算规则,做出题目简直太简单了。
例子:[x]补=0.0101,[y]补=1.0101,求[x*y]补。
定点数的除法
这篇文章的内容已经比较多了,为了方便读者消化知识,除法的讲解放在下一篇文章中,敬请期待。
结语
如果你喜欢我的文章,请关注我的微信公众号“最高权限比特流”吧!
【参考文献】《计算机组成原理》,唐朔飞。
上一篇: 原码与补码的单位位乘法详解
推荐阅读
-
聊聊计算机构造基础(第九章):定点数值与定点计算的操作
-
聊聊计算机组成基础(9):定点数值计算 - 理解定点数的位移运算
-
数的机器码表示:原码、反码、补码、变形补码、移码和浮点数编码-数学定义:例:+111的原码为0111,-101的原码为1101 (2) 纯小数的原码表示 纯小数的原码首位同样为符号位,后面的数值则表示小数的尾数,纯小数的整数位为默认为0无需表示。 例:+0.111的原码为0111,-0.101的原码为1101 可以看到,+111和+0.111的原码同为0111,这是因为约定的小数点位置不同,整数的原码的小数点约定在末尾,纯小数的原码的小数点约定在数值的最前面,这样通过约定小数点的位置来表示数的方法就称为定点数表示法,约定小数点位置实际上就是约定编码中每一位的权重。 二、反码 正数的反码与其原码相同。 负数的反码是其对应原码的符号位不变,数值位按位取反。 数学定义:例: 真值 +111 -101 +0.111 -0.101 原码 0111 1101 0111 1101 反码 0111 1010 0111 1010 三、补码 原码虽然转换很简单,但是在做减法时操作很复杂(减不够还要借位),因此计算机在做加负数操作时会先将负数的原码转换为补码再做加法。 先举个栗子,假设时钟现在是9点钟,我把时针往回拨3个小时是6点钟,或者顺时针往后拨9个小时还是6点钟,也就是说9-3的结果等同于9+9(mod 12),对于模数12,-3的补码为+9,这就引申出了一种将减法转换为加法的思想,把减去一个正数视为加上一个负数(例如9+(-3)),再将负数转换为对应的补码,最后就可以和补码做加法了,若结果超出了模数则丢弃一个模数即可。 如图所示:9减去灰色的部分(-3)就等同于加上蓝色的部分,即-3的补码即为蓝色部分的长度9(mod 12)。即补码=模数+真值(超出模数则舍弃一个模数) (1) 整数的补码表示 对于一个n位的二进制真值x,则取模数为2^(n+1),若x为正数则补码和原码相同(加上一个模数又需舍弃一个模数 故相同),若为负数则补码为模数加上x。相对于原码,补码这里的首位就不仅代表原数真值的符号了,也是补码自己的一个数值位。 取模数为2^(n+1)是因为在需要舍弃模数时只需要舍弃运算结果(二进制数)的最高位即可,这在计算机中很容易实现 数学定义:例:三位二进制数的模数2^4就是10000,故+111的补码为0111(即10000 + 111 = 0111 (舍弃模数位)),-101的补码为1011(即10000 - 101 = 1011) 补码运算示例:那么+111 - 101 = +111 + (-101) = 0111 + 1011 = 10010,运算结果只保留后四位(即舍弃模数位),故计算结果为0010。这样就通过加法实现了减法运算。 补码可表示数据范围:由数学定义可知,n位二进制补码可表示的数据范围为 -2n-1~2n-1-1。以8位的byte类型数为例,可表示的数据范围为 -27~27-1,即-128至+127,最小负数-128(补码:1000 0000),最大负数-1(补码:1111 1111),0(补码:0000 0000),最小正数1(补码:0000 0001),最大正数127(补码:0111 1111)。 由补码求真值:正数的补码即为原码即为真值,负数的真值由计算规则可知 负数真值= - (模数 - 补码),以补码1111 1111为例,其真值 = - (1 0000 0000 - 1111 1111) = - 0000 0001 = -1 (2) 纯小数的补码表示 对于一个纯小数x,则取模数为2^1,正数的补码和原码相同,负数的补码为模数2加上x。同样补码的首位不仅代表原数真值的符号,也是补码的数值位。 数学定义:例:纯小数的模数2就是10,故+0.111的补码为0111,-0.101的补码为1011(小数点约定在符号位后) 计算机中求补码的规则 可以注意到求负数的补码时还是要做减法,这在计算机中就很不方便了,但是通过其数学定义可以看到无论是整数还是纯小数,负数的补码都等于反码的末尾加1,而这又等同于原码数值位从右向左遇到第一个1后,这个1左边的数值位都按位取反,故实际计算机中求补码的规则如下:正数的补码等于原码负数的补码等于原码的数值位从右向左的第一个1左边的所有数值位按位取反(例:byte类型值-6的原码为1000 0110,则其补码为1111 1010) 四、变形补码 两个补码在运算时可能会溢出从而产生错误的结果,比如0111+0101 = 1100,两个正数相加反而得到了一个负数,那么在计算机中要如何判断运算结果是否溢出了呢,这就引申出了变形补码。从直观上看,相对于补码来说变形补码就是用两位来表示符号位,00表示正数,11表示负数。运算结果符号位为01表示正溢出,10表示负溢出。