深入理解计算机系统之浮点数

一、什么是浮点数

了解浮点数这个概念前,先要了解什么是定点数,定点数的概念为:

定点表示即约定机器数中的小数点位置是固定不变的,小数点不再使用“.”表示,而是约定它的位置,即在固定 bit 下,约定小数点的位置,然后把整数部分和小数部分分别转换为二进制,就是定点数的结果。

而浮点数的概念即是与定点数相反的,浮点数的小数点位置不是固定的,当然这是一句废话。

接下来重点来了,浮点数小数点的浮动性怎么体现呢?

这里就引入一个名为科学计数法的概念:(十进制下)

科学记数法是一种记数的方法。 把一个数表示成a与10的n次幂相乘的形式

(1≤|a|<10,a不为分数形式,n为整数),这种记数法叫做 科学记数法。

例如:十进制小数 1.2345 我们可以用多种形式的计数法表达这一个数:

1.2345 = 1.2345 * 10 ^ 0

1.2345 = 12.345 * 10 ^ {-1}

1.2345 = 123.45 * 10 ^ {-2}

1.2345 = 1234.5 * 10 ^ {-3}

...

诸如此类的形式,我们会发现不同表达方式下,小数点的位置都是不同的,我们可以由10的多少次幂(后会说明此为阶码)来移动这个小数点,但表达的都是同一个数,浮点数的概念由此而来。

上述的科学计数法是基于十进制下表现的,我们以此进行展开,同样可以得到其他进制下的科学计数法的表达方式。

比如二进制下小数 10.111 我们用二进制科学计数法表达它:

10.111 = 1.0111 * 2 ^ {1}

10.111 = 10.111 * 2 ^ {0}

10.111 = 101.11 * 2 ^ {-1}

10.111 = 1011.1 * 2 ^ {-2}

...

现在我们已经对浮点数的概念应该已经有了一个比较理性的认识.

二、计算机如何表达浮点数

        我们知道在计算机内所有的指令和储存都是基于二进制的 01 串构成,那么浮点数也不例外,根据上文,我们可以科学计数法表达所有的小数,它的形式如下:


N = Base * Base system ^ {power}

即 浮点数 = 基数 * 进制 ^ 幂次

化为更一般的形式为:
 

N = (-1)^{S} * M * R^{E}(公式1)

其中 S 表示符号(Symbol), M 表示为尾数(Mantissa)(即小数点应该放在哪位,对应于某一种浮点数的科学计数法的表示方式),R 表示为基数(Radix)(不同进制下的阶数不同,如十进制下 R = 10, 二进制下 R = 2),E 表示的就是阶码(Exponent)(其实就是指数啦)。

 例:-1.2345 = (-1)^1 * 1.2345 * 10 ^ {0}      (十进制)

                原数 = (-1)^ 符号 * 尾数 * 10 ^ 阶码

————分割线(可以直接跳到下一个分割线下面)

通用形式我们表达出来了,计算机如何表达他们呢?接下来的过程有点抽象,但是我们只需要明白一点,计算机只是一个计算工具,它所有表达出的功能都只是我们人为的对其解读数据方式进行规范,所有命令在计算机内的本质都是 01 串

比如说 1 ,我们可以对 1 解读为 1 个 10,1 个 100,...,那 1 具体表达什么我们就需要从特定的场合中去理解,这就是一个抽象的过程。懂得了这里,那么下面的规则也就不难理解了。 

————分割线

        我们知道计算机储存一个 (int) 型整数是需要 32 位 bit 进行储存,如果32位不够用我们还有64位 bit 的 (long) 型整数;而浮点数也是用同样的方式去存储的,它就分为了32位的 float (单精度),和64位的double (双精度)这两种类型 (此处的概念源于第三节浮点数标准)。

下面先给出二进制和十进制小数转化的概念,为了理解方便,依旧采用类比的方式。

对于十进制数(1.12)我们可以把这个数分为整数部分和小数部分

那么二进制数(11.11)同样可以分为整数和小数部分

我们知道整数部分二进制转化为十进制是通过给每一个二进制位赋权相加而来

如:二进制数 (11) 转化为十进制:

N = 1 * 2^1 + 1 * 2^{0}

那么小数点后的数 ( .11) 我们同样是通过赋权方式将这个二进制小数转化为十进制:

N = 1 * 2^{-1} + 1 * 2^{-2}

那么对于二进制小数(11.01)转化为十进制就有:

N = 1 * 2^1 + 1 * 2^{0} + 0 * 2^{-1} + 1 * 2^{-2}

我们可以发现,二进制并不能非常精确的表达十进制数,它能表达的小数只能由二的负次幂组合而成,下面给出每一位二进制小数位对比于十进制:

2^{-1} = 0.5

2^{-2} = 0.25

2^{-3} = 0.125

2^{-4} = 0.0625

...

所以想完全精确地表达十进制小数诸如 (10.333) 这类型的整数是不可能的,那么为了缩小表达的误差,我们只能划分更多尾数位去表达这个数减小误差,而尾数的位数变多,就会导致阶码的位数变小,即表示的范围变小(推论1.

        回归正题,我们现在要用计算机的 32位bit (单精度)去表达一个小数,那么我们必须告诉计算机一个解读二进制串的规则,否则计算机就单纯只能表达01串;根据科学计数法的公式1,我们可以知道我们需要用一个 bit 去表达符号位(S),那么我们需要多少位去表达它的尾数部分和它的阶数部分?

现在我们不妨规定:在32位下,我们用1位表示符号,10位表示整数,21位表示小数(规则1),现在给出一个十进制小数,请你根据规则解读他为二进制串:


 

例:-10.25

整数部分:10 —— 对应二进制 1010

小数部分:0.25 —— 对应二进制 .01

符号部分:-1 —— 对应二进制符号位 1

得到二进制串 1010.01(符号位不在内)

写成科学计数法的形式 有下式:

N = (-1)^1 * 1.01001 * 2^3

得出 S = 1,M = 1.01001,E = 3 (注意此时的E是十进制下的表达,写入阶码段时要转换)

二进制下这三十二位表示为:

看看我们从十进制小数转化为二进制经历了什么:

十进制小数——划分部分——将对应部分转化为二进制——组合得到的各部分二进制串——运用科学计数法表达得到的二进制串——将对应的S,M,E写入32位串对应位置中——得到答案

是不是比较轻松?

完成这部分就代表了我们很轻易的就创造了一种可以交给计算去执行十进制小数转化为二进制小数的规则1

那么接下来根据这个规则1学习将二进制转化为十进制吧!

为方便阅读,S,E,M之间我们用 " | " 符号分开

 例:1 | 0000 0000 11 | 1100...00

得到 S = 1,E = 3,M = 1.1(E为十进制形式)

由十进制下的科学计数法转化得到二进制串 1100.0

由二进制

整数部分 1100 —— 对应十进制 121

小数部分 0 —— 对应十进制0

符号位 1 —— 对应 负号

所以得到十进制整数 -12.0

经过二进制到十进制的转化,我们会发现这个过程就是十进制转化为二进制的逆过程(废话),了解这些,我们就基本掌握计算机在规则1下转化浮点数的过程了,吗?

        其实仔细的同学会发现我们的规则1非常的不完善!存在很多优化的方法,接下来就是很重要的完善方式,一定要理解。

完善1:

阶码的完善:

        我们会发现对于我们对于阶码的解读只存在正数,没有负数的表示,考虑一下,我们如果有负数的阶码能得到什么?能表达更小的数!即精度更高,下面给出解释;

        对于规则1来说,我们如果用10位阶码的最高位表达符号位,其他不变根据公式1可推出,我们能表达的整数范围变小了(从0 ~ 2^{10} - 1 到  0 ~ 2^{9} - 1),但使得数的表达一下子多出了 2^{9}-1 的精确度。

        我们使用小数肯定是希望越精确越好,所以为了更高的小数精确度,我们会使得阶码的最高位为符号位,因此我们得到 E 的范围为(-2^{9} + 1 ~ 2^{9} - 1).

完善2:

精度的完善:

        对规则1来说当我们计算21位的 M (尾数)时,我们会发现它前面总会有一个1存在(特殊情况暂不考虑),且我们总会把这一个1存进计算机里面,如果我们把这个 1 隐去,就能够很轻易的获得22位的精度,这种方式也叫隐含以 1 开头的表示

...

        其实还能完善的有很多,但是要记住,以上只是我们个人设计的一种二进制串的解读方式,如果我们每个人对计算机设定的解读二进制串的方式不同的话,就会导致不同计算机之间的数据移植变得困难,且不同的解读方式根据推论1可知得到的精度和范围也有很大的差异。

为了减少不同规则之间转换的计算成本,我们需要一个统一的标准

三、浮点数标准

        1985年IEEE推出了统一的浮点数标准,并给出了两种浮点数的格式:
1.float单精度类型:符号位占1位,阶码占8位,尾数占23位

2.double双精度类型:符号位占1位,阶码占11位,尾数占52位

        不仅如此,IEEE为了使表达更精确,还给出了很多的完善规则:

如:

1.我们上面说的 隐含 1 开头的表示,都可以使上述两种浮点数尾数精度加1

2.推出规格化数和非规格化数

我们发现如果不是所有的小数都需要隐含开头的1,所以IEEE对两者规定如下:

规格化数:阶码位不全为 1,也不全为0的数称为规格化数

        类比我们之前创造且完善了一部分的规则1,这里IEEE对阶码段的解读方式和我们的不一样,IEEE规定阶码段表达的二进制数为无符号数,那我们读取这个数时最大值就变成了255,和我们未进行完善2的规则1相比是一样的,但是我们采取的是最高位置为符号位,IEEE则设置了一个偏置值(bias),让 E = e - bias(在单精度下bias = 127, 双精度下为bias = 1023),其中 e 等于我们读到的阶码二进制无符号数,这使得单精度下E的表达范围为(-126 ~ 127)(注意这里计算出的范围是去点全1和全0的E).

且隐含 1 开头的表示只对规格化数生效.

非规格化数:阶码位为全0的数表示为非规格化数

        非规格化数的存在是为了表达规格化数表达不了的更小的数,由于阶码全为0,类比于规格化数的表示,它的E应该有 E = - bias,这样的表达,但是IEEE却规定 E = 1 - bias,这样反直觉的设定且使其减小了一个精度的表达方式好像不符合常理,其实,多加的这个1提供了一种从非规格化数平滑的转化到规格化数的方式(下面有给出伪证明). 所以对于单精度的非规格化数E = -126 .

看到这里,我们会发现,啊这单精度的E的值不就在规格化E的表达范围里面吗?拿要这非规格化数有何用?

其实我们可以注意到,非规格化数给我们提供了一种表达0的方式,(由于我们为了多一个位的精度,隐去开头1造成的连锁反应),因为规格化数隐去1的开头,所以其不能表达 0 这个数;且由于规格化隐去了1开头,那么我们想用规格化数表达 非常趋近于0的数 的话显然就不是特别理想了,而非规格化能表达没有开头1的这种数,即有无限趋近0的数的这种属性(在csapp种称这种属性为逐渐下溢,感觉不讲人话)

那么有规格化和非规格化这两个数合在一起,我们就能表达非常精确的小数了,那两者合在一起取交集,如果我们之前非规格化数的E没有加1,计算结果为-127的化,虽然说精度范围增加了一位,但是由于规格化数E的范围只为(-126 ~ 127),这就代表我们取两者表达的交集的时候 - 127 到 -126 是一道坎,如果把它两的表达范围画个图的话,就会出现一个断层,大概就是下图黑线所示(此处图片只为形象解释)

红线则可以视为对非规格化数的偏置加上1的平滑表示

由此我们基本可以了解规格化数和非规格化数的含义及区别

此外IEEE还规定了几个特殊的值

注:此类值只针对阶码全为1的值

正无穷:尾数全为0,且符号位为0;

负无穷:尾数全为0,且符号位为1;

NaN:当尾数值不全为0,"not a number" 即不是一个数 如:计算\sqrt{-1}就会返回NaN.

以上就是对IEEE规定的浮点数标准进行的总结,我们试着类比于使用规则1的方式,使用IEEE标准实现十进制小数和二进制之间的相互转化。(下面实例都为32位单精度下的转化)

重点:考试就考下面的转化

例:-10.25

整数部分:10 —— 对应二进制 1010

小数部分:0.25——对应二进制 .01

符号部分:-1 —— 对应符号位 1

得到二进制串:1010.01

使用科学计数法表达:

N = (-1)^1 * 1.01001 * 2^3  //到这一步为止,其与我们自定的规则计算的都是一样的

得到 E = 3,由 E = e - bais 我们得到阶码段的数位 e = bais + E = 127 + 3 = 130

所以 e 对应的二进制阶码段为:1000 0010

尾数段W对应的二进制串为:01001 (注意,由于不是趋近于0,所以使用的是规格化数隐去1规则)

所以填充进32位串里得到:

小tips:

        当我们转化的十进制过小时,我们会发现规格化处理不了它,那么就要使用非规格化处理它 

 

同样二进制转十进制

 例:1 | 1000 0000 | 1100...00

//注意这里转换尾数时,我们必须要知道这个串是按照规格化解读还是非规格化解读,这关乎到小数点前面是否读出1,这里看阶码段是不是全为0,全为0就是非规格数,这里显然是规格化数//

得到 S = 1,e = 128,M = 1.11(e为十进制形式)

而 E = e - basi = 128 - 127 = 1

由十进制下的科学计数法转化得到二进制串 11.1

整数部分 11 —— 对应十进制3

小数部分 .1——对应十进制0.5

符号部分 1 ——对应符号 -

所以得到十进制下的该数 -3.5

至此浮点数的基本内容就基本讲解完毕了,文中很多描述都带有作者自己的主观想法,且证明方式都是伪证明,并没有给出具体原理,所以肯定存在些纰漏和不足,望理解。

  • 9
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 《深入理解计算机系统》是一本经典的计算机科学教材,第二版是对第一版的全面升级和扩展。本书的作者是布莱恩·卡尼汉(Brian Kernighan)和丹尼斯·里奇(Dennis Ritchie),他们是C语言的创始人之一,有着丰富的实践经验和深厚的理论功底。 这本书的主要目的是帮助读者深入了解计算机系统的底层原理和工作机制,以及如何利用这些知识进行系统性能优化和问题调试。书中从编程语言、汇编语言、程序执行、内存管理、文件系统等多个角度,详细介绍了计算机系统的各个组成部分和其相互作用的方式。 第二版在内容和理念上都有了一些新的改进。首先,本书增加了关于网络编程和并发编程的新章节,以适应当今计算机系统日益复杂的发展趋势。其次,为了保持与迅速变化的技术形势的同步,第二版对许多章节进行了修订和更新,包括对新的计算机体系结构、处理器技术、存储介质等的介绍,并针对一些近期出现的漏洞和攻击做了相应的解释。 本书的特点是理论与实践相结合,既深入剖析计算机系统的原理,又通过大量实例和案例帮助读者将理论应用到实际问题中。读者在阅读过程中,不仅可以学到计算机系统的原理和工作机制,还能够锻炼自己的系统设计和调试能力。 总的来说,《深入理解计算机系统》第二版是一本非常重要的计算机科学教材,适合对计算机系统工作原理感兴趣的学生、教师和从业人员阅读。通过阅读本书,读者可以深入理解计算机系统的底层原理,提高系统性能,解决实际问题。 ### 回答2: 《深入理解计算机系统》第二版是一本经典的计算机系统原理教材,由美国卡内基梅隆大学的教授布莱恩·卡尼汉(Brian Kernighan)和罗伯特·戴维森(Robert Davidson)合著。本书的中文翻译版本在CSDN上非常受欢迎。 该书主要通过深入讲解计算机底层硬件、操作系统以及编译原理等知识,帮助读者全面理解计算机系统的工作原理和设计思想。书中内容围绕计算机系统的核心概念展开,包括进程、内存管理、文件系统、虚拟内存等。 《深入理解计算机系统》第二版与第一版相比,进行了全面的更新和改进。作者引入了最新的计算机体系结构和技术,在保留经典内容的基础上,增加了对多核处理器、并行计算等新技术的讲解。此外,书中后期的内容还涉及了网络编程、安全和性能优化等实际应用方面的知识。 这本书的优点在于,作者以清晰简洁的语言,结合大量实例和案例,将复杂的计算机系统理论概念讲解得容易理解和易于实际运用。读者通过学习本书,可以更好地理解和分析计算机系统的性能瓶颈,并通过优化和改进提升系统的效率。 此外,《深入理解计算机系统》还鼓励读者通过自主实践,使用常见的工具和技术,动手实践并深入理解计算机系统设计和性能调优的方法。这种实践性的学习方式,使得读者能够通过实际操作加深对书中知识的理解和掌握。 总之,《深入理解计算机系统》第二版通过全面深入的讲解,帮助读者建立起系统化的计算机系统知识框架。对于计算机科学相关专业的学生和从事软件开发、系统管理等工作的人员来说,本书都是一本非常有价值的参考资料,有助于他们理解计算机系统的内在原理和工作机制,进一步提升技术水平。 ### 回答3: 《深入理解计算机系统(第二版)》是由美国卡内基梅隆大学的教授Randal E. Bryant和David R. O'Hallaron合著的一本计算机系统相关的教材。该书是计算机科学与工程领域的经典教材之一,旨在帮助读者深入理解计算机系统的底层原理和工作机制。 这本书主要分为10个章节,从CPU的组成部分开始,逐步向上层的内存和I/O系统扩展。第一章介绍了计算机系统的基本概念和层次结构,为后续章节奠定了基础。接着,第二章到第五章讲解了整数和浮点数的表示与运算,同时介绍了汇编语言和数据表示的相关概念。 在第六章和第七章中,书籍聚焦于理解计算机系统中内存层次结构和缓存一致性。这些章节解释了为什么程序中有些内存操作会比其他操作更快,并介绍了各种优化技术。在第八章中,书籍介绍了虚拟内存的概念与实现方式,深入讲解了操作系统如何使用虚拟内存机制提高程序的执行效率。 接下来的两个章节,第九章和第十章,介绍了动态内存分配和链接。这些章节探讨了程序运行时如何管理内存和使用动态分配的技术,如何生成可执行文件并将其与其他对象文件链接。 《深入理解计算机系统(第二版)》通过系统性的讲解,帮助读者逐步深入理解计算机系统的底层原理。书中的例子和实践问题,能帮助读者巩固所学知识并应用于实际问题。此外,书的附录还提供了一些计算机系统方面的背景知识,供读者参考。 通过阅读这本书,读者可以全面掌握计算机系统的基本原理,并具备实际解决问题的能力。无论是对于计算机科学与工程专业的学生,还是对于从事计算机系统相关领域的从业者来说,《深入理解计算机系统(第二版)》都是一本必不可少的参考书籍。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值