计算机组成原理白学了,再次回顾浮点数加减

2023.12.06更新

感谢评论区 @做而论道_CS 大佬的指正,更正内容如下:

0.5和-0.4375的和(单精度浮点数)

首先写出两个数的二进制原码,根据1+8+23的形式写出

0 0111 1110 00000000000000000000000
1 0111 1101 11000000000000000000000

0.5的阶码十进制为 126 阶为 126-127=-1
-0.4375的阶码十进制为 125 阶为 125-127=-2

小阶对大阶 所以-0.4375向0.5进行对阶 右移(-1+2)=1位

-0.4375的尾数向右移1位

后面的尾数右规和尾数相加都是在补码的条件下进行 需要对-0.4375求一下补码

尾数还包含隐藏位1

尾数原码 111000000000000000000000
尾数补码 001000000000000000000000

对尾数进行右规,右移一位(算术右移,最高位补1)

100100000000000000000000(0)

开始进行尾数相加

00-100000000000000000000000
11-100100000000000000000000(0)
100-000100000000000000000000(0)

100溢出截断

00-000100000000000000000000(0)

在尾数相加完之后再进行舍入操作,0舍1入,括号里的是0,则舍去得
00-000100000000000000000000

尾数最高位和符号位相同,左规三位,阶码为126-3=123 阶为123-127=-4

00-100000000000000000000000

因为最终是正数,所以正数补码即为本身也即是原码

去掉隐藏位1则得结果为

0 0111 1011 00000000000000000000000

即 1.0*2^(-4) = 0.0625

说明几点:

  1. 补码的左右移位均为算术移,即负数右移最左边添1,正数右移最左边添0;负数左移,最右边添0,正数左移,最右边添0
  2. 尾数求补码的时候包含隐藏位
  3. 舍入操作一般在右规的时候,但为了避免重复右规,一般在尾数相加后才进行舍去操作
  4. 0舍1入指的是由于右移移出的数字(上面括号中的数字),从左往右数,最高位是0则舍去,最高位是1则在最低有效位加一
  5. 按照上面的原则去读最后两个例子是完全能够解释的,并且得到的二进制浮点数能够还原十进制结果
  6. 如果还有其它错误欢迎广大网友批评指正

大二学的计算机组成原理,回顾下其中的知识

一个加法引发的思考

  上次在一个论坛里看到了Java里的一些不正常的操作,比如说0.05+0.01的结果居然不是等于0.06,截图为证:(虽然说在学计算机组成原理的时候貌似也碰到过,但是那时并没有深入研究)

1587277442111923.jpg

1587277470994802.jpg

  于是我便就此问题进行深入的探讨,不过却发现要计算机组成原理的相关知识,大二学的计算机组成原理,现在基本上全部还给老师了,于是重新回顾下计算机组成原理里相关的知识。

计算机中的原码、反码、补码、移码

原码

  在计算机中增加了一个符号位来表示数字的正负值,符号位为0表示正数,符号位为1表示负数,是在其数值的基础上加了符号位,举个栗子:

  • 123的原码为:0111 1011,第一个0表示符号位,后面的就是其二进制数值。
  • -123的原码为:1111 1011,第一个1表示符号位,后面的就是其二进制数值。

  早期就是用原码来表示数字的,但是这样会出问题,举个例子,假如用8位二进制数来表示一个数,那么1的原码为 0000 0001,那么-1的原码为 1000 0001,正常来说 1+(-1) = 0,但是如果用原码来进行相加的话,结果就是 1000 0010 ,用原码来表示也就是-2,和我们正确的结果不一样,这就是原码在计算上的缺点。同样用原码来表示数会出现+0和-0的情况,但是我们的0只有一个。

反码

  反码也就是在原码的基础上做了一些改变,正数的反码就是其原码,负数的反码就是其原码除了符号位之后各个位取反。举个栗子:

  • 123的反码为:0111 1000,也就是其原码。
  • -123的反码为:1000 0111,也就是除了符号位之外,每一位取反。

补码

  补码是在反码的基础上增加了一些规则,正数的补码是其本身,也就是其原码,而负数的补码也就是除符号位之外,其余各位取反,取反之后的结果加一(负数的补码相当于在其反码之后加一)。举个栗子:

  • 123的补码为:0111 1000,也就是其原码。
  • -123的补码为:1000 1000,也就是其反码加一。

移码

  移码的计算是在补码的符号位上进行取反,举个栗子:

  • 123的移码为:1111 1000。
  • -123的移码为:0000 1000。

为什么计算机中要用这些来表示数?

  计算机不能够像人一样直接识别10进制的数字,像1、2、3、4、5、6等,计算机中只能识别二进制的数据,所以计算机中的数据存储是以二进制为基础的,既然这样,那么就涉及到二进制数据的加减乘除,那么计算机中是如何进行处理的呢?

  我们还是回到刚才讨论的1+(-1)的问题,也就是1-1的问题,如果原码表示就会出现0000 0001+1000 0001 = 1000 0010,从而得出-2的结果,这显然是不正确的,所以我们引入了反码,将二者的反码相加,0000 0001+1111 1110 = 1111 1111,然后将结果从反码转换为原码,也就是1000 0000,也就是-0,这样解决了原码减法的问题,但是还是存在问题,也就是+0和-0,在人类的认知中+0和-0是一样的,所以在计算机中也只能有一种表示。

  由于反码无法解决+0和-0的问题,我们才引入补码的概念,在补码里,+0的补码为,0000 0000,-0的补码为(1000 0000)原码 ——>(1111 1111)反码 ——>(0000 0000)补码,从其反码到补码的过程由于加一之后溢出,而8位二进制数只能存储8位,所以溢出的丢弃,所以在补码里,+0,和-0的补码是一样的。

  值得一提的是几个特殊数的补码,-128的补码是 1000 0000,而我们知道原码表示的时候,最高位是要用作符号位的,所以8位二进制数的原码表示范围为-(27-1)-(27-1)也就是(-127~+127),所以-128的原码是无法直接写出来的,既然这样,那-128为什么还有补码呢?

  在解决上面这个问题之前,我们先来了解一下同余定理的概念,假如有两个数a和b,如果(a-b)能够被m整除,那么就称a和b对模m同余,这就是同余定理。现在我们回到之前,补码的提出是为了解决两个问题,第一个是计算机里的减法,将减法变成加法来做;第二个问题就是+0和-0的解决。而补码的提出就是为了让负数变成能够加的正数。这里我们举个栗子,我们知道那种挂钟是以12为单位的,如果现在是8点,我们想将其变成5点,有两种方式,第一种是顺时针拨动9格,另外一种是逆时针拨动3格。逆时针拨动3格很容易理解,就是8-3=5,而顺时针拨动9格即是8+9 = 8+(12-3) = 17=12+5。两种方式都能够得到结果,但是第一种方法采取的是化减为加的方法。

  那么我们可以借鉴这样的思路来实现计算机中的减法变加法,通过以上的介绍我们很容易知道8位二进制的模为256,因为满256就进位。那么,我们来计算1+(-2)的时候,就可以计算1+(256-2)=255,而255的表示为1111 1111,刚好就是结果-1的补码((-1)——>(1000 0001)原码 ——>(1111 1110)反码 ——>(1111 1111)补码)。

  那么也就是说,求一个数的补码还可以这样求,一个数的补码 = 模-这个数的绝对值。既然如此,我们就可以求-128的补码,-128的补码等于256-128,而256-128得到的结果是128,也就是(1000 0000),所以-128的补码是(1000 0000)。而我们知道,能够通过一个数的补码从而求其原码,方法是符号位不变,其他位取反,然后末尾加一,当对符号位有进位时,进位忽略(当然这是针对符号位为 1的,若符号位为0,那么原码即是补码)。既然如此那么我们根据-128的补码求其原码,(1000 0000)补码 ——>取反(1111 1111)加一(1000 0000)原码也就是说,-128的原码是-0,不过的确是这样,因为在补码里+0和-0的补码是一样的,所以选择其中一个表示0,当然+0就被选上了,那么多出的一个自然就被用作-128,所以8位二进制数的补码表示的数字要比原码和反码多一个-128。

  所以我们来做一个小总结:(基于8位二进制数)

  • -128的补码是[1000 0000],没有原码(根据补码求得的原码在原码看来是-0),没有反码。
  • +128在8位二进制数中是没有原码、反码、补码。
  • +1的原码是[0000 0001],反码是[0111 1110],补码是[0000 0001]。
  • -1的原码是[1000 0001],反码是[1111 1110],补码是[1111 1111]。
  • 0的原码是[0000 0000],反码是[0111 1111],补码是[0000 0000]。

计算机中如何表示小数?

  在计算机组成原理这门课中我们学过,浮点数的表示有分为32位浮点数的表示和64位浮点数的表示,他们的表示方法如下图:

1587266419081361.jpg

1587266541899709.jpg

  上面的两张图分别表示的是32位浮点数在计算机中的表示和64位浮点数在计算机中的表示,这里的阶码就类似我们使用科学计数法的指数,举个栗子,123我们用科学计数法表示为1.23×102在这里2就是阶数,而我们在利用科学计数法进行加减的时候是这样进行的,比如说1.23×102+2.556×103,我们的做法是通过移动小数点将它们的阶数变成一致,然后再进行相加。而我们通常会把小的阶数转化成大的阶数,然后再进行相加,所以转化成0.123×103+2.556×103=2.679×103

  而在计算机中同样如此,我们之前讨论过了,在计算机中是以补码的形式进行加减操作的,所以如果阶码也是用补码的话就造成了一个问题?上面我们说过我们在进行数字的相加的时候要进行对阶操作,也就是将阶数小的转化成阶数大的(这里可能有同学会问为什么不将大阶对小阶,你可以试一下,原因是大阶对小阶造成的数字丢失要比小阶对大阶要大)。那这样的话,首先要进行阶数的比较,不然怎么知道谁大谁小,如果阶码采取补码的方式,那么在一个浮点数里就有两个符号位,这样不便直接通过无逻辑的二进制进行比较,所以阶码这里不能采取有符号数,只能采取无符号数,既然这样我们为什么要用移码来表示阶码呢?

  这样,我们先来提出机器零的概念,在计算机中是不能够准确的表示自然界中的0的,只能表示一个比较接近于0的数,所以在计算机中,当一个数小于计算机能够表示最小数的时候,计算机就认为这个数为0,这就是机器零,而我们知道浮点数的表示方法(32位浮点数)为(-1)S1.M×2E-127 ,其中S位符号位,M位尾数,E为阶码。所以当尾数全为0的时候不论阶码多少,这个数为0,而当阶码小于能表示的范围的最小值的时候,不论尾数为多少,这个数为0(这是因为当阶码小于能够表示范围的最小值的时候,那么整个数也就小于能够表示范围的最小值了,自然也就当0看待,那么就不用了管尾数的多少了)。

  弄懂了机器零的概念,我们再回到上面讨论的用移码来表示阶码的问题。我们知道机器0在二进制的表示上是不为0的,只是在概念上为0,那么为了使机器零在表示上为0,我们引入了移码,移码在原来补码的基础上符号位取反,相当于是最高位加1,等同于整个数在原来的基础上加了2n-1其中n表示的是二进制数的位数。那也就是说在32位浮点数的里原来的数加上128就能够得到阶码了?实际上不是128而是127,那这又是什么原因呢?如果单纯的按照移码的定义,那么偏置为128能够表示的数的范围为-128~127,但是要注意0000 0000和1111 1111,这两个数分别表示非规格化数和无穷大,也就是两个边界状态,那也就是说我们实际的移码范围是0000 0001到1111 1110,也就是1到254,如果采取偏置为128那么,表示的范围是-127到126,和原来的范围相比左右各收缩一个单位,如果偏置为127,那么表示的范围是-126到127,和原来的范围相比,负数表达的范围小了,正数表达的范围没变,我们知道,在计算机中一个更大的数的表示比一个更小的数的表示要重要,所以在这里我们的偏置取127,也就是2n-1-1,那么在64位浮点数里偏置自然也就是1023了。

浮点数的转换以及加减

  通过上面知识的回顾,我们来几道题目练下手,加深印象。

  • (-0.75)10转化成IEEE 754单精度浮点表示

1587279275956623.jpg

  • 求 C0A00000H对应的IEEE 754单精度浮点数的值为多少?

1587280153168936.jpg

  • 计算两浮点数的和,均是10进制,0.5和-0.4375之和。(单精度浮点数)

1587388120447885.jpg

回到本文开头提出的问题

  那么我们只需要将0.05和0.01分别用64为浮点数表示,之后再进行相加即可,步骤如下:

1587388164428310.jpg

  在将0.05化成二进制小数的时候采用了如下Java程序辅助计算:

1587366451364680.jpg

  在此之后笔者又找到这样的例子,并用刚才的方法进行验证,最终得出正确的结果,如下:

1587366557392178.jpg

1587388223249558.jpg

一些规则

  • 进行浮点数加减的时候首先是对阶,对阶是小阶对大阶,小阶差大阶多少,小阶的尾数向右移多少, 在右移的时候,最高位1被移出,最高位补0。
  • 进行尾数相加的时候是补码相加,IEEE 754里尾数是原码表示的,所以我们要化成补码然后相加,正数的补码即其本身,负数的补码符号位不变,其它位取反,最后再加1。而且在尾数相加的时候采取的是二位符号位,00表示正数,11表示负数。(注意:这里的尾数是要包含隐藏位的)
  • 当尾数相加的时候,溢出了(符号位出现01或者10的时候),当符号位为01的时候表示上溢,符号位以0为准,为正值,不过溢出之后要做右规操作,然后做舍入操作,舍入操作即是0舍1入。
  • 当尾数相加,符号位为10的时候表示下溢,符号位以1为准,为负值,溢出之后做右规操作,然后做舍入操作,舍入操作即是0舍1入。
  • 当尾数相加没有出现溢出的时候,此时如果最高位和符号位相同,那么需要对尾数做左规处理,直到最高位与符号位数字不同。

参考文章

细说浮点数

浮点数的加减法

浮点数的运算步骤

浮点数运算中的舍入问题

为什么说浮点数缺乏精确性,知乎陈清扬回答

【南京大学计算机组成原理】袁春风

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值