本文深入探讨了计算机中数字的表示方式,重点讲解了IEEE-754标准的由来及其规定,包括浮点数的组成、正规化、移码、非正规化等概念,以及浮点数的运算规则。
本文主要简述了计算机中数字的表示方式,以及IEEE-754标准的由来及具体规定,最后简单的叙述了浮点数的运算规则。阅读这篇文章之前,最好有关于有关于机器数中的原码、反码、补码三种形式的一些概念。
计算机内数据和指令都是由晶体管和门电路等元件完成的,对于这些元件来说,开或者关是其唯一的状态,这种状态的表现就是二进制的理念。就像黑客帝国世界中漫天飞的0和1一样,计算机世界使用的机器语言也只有0和1。而在机器语言中,当计算机想要表示一个数字时,这时就得使用机器数了,机器数所表示的真实数值称为真值。
机器数(computer number)是将符号"数字化"的数,是数字在计算机中的二进制表示形式。机器数有2个特点:一是符号数字化,二是其数的大小受机器字长的限制
机器数有两个特点:
那么我们以8bit二进制为例,看看机器数原码如何表示10进制中的3和-3。
对于正数符号位为0,所以机器数为00000011
对于负数符号位为1,所以机器数为10000011
在上文中,我们知道计算机通过二进制可以精确的表达整数,那么计算机是如何表达小数,或者说计算机是如何处理这些小数的呢?
在早期,计算机表示小数的方式是定点小数的方式去表示的,定点小数中。小数点隐含在第一位编码和第二位中间。
如真值为正值的小数0.101,使用定点小数在计算机中表示就为0 101
然而,早期使用定点小数无法表示过大和过小的值,并且在计算过程由于数值范围的限定,会出现数值溢出的问题。
随着技术的更新,在1978年的时候,Intel公司推出了首枚16bit微处理器(CPU)8086。这台x86的老祖宗虽然自身无法处理小数的运算,但是在编译器层面可以通过用整数指令模拟出小数的运算,不过这种运算的方式效率是非常低的。
为了解决这类问题,1980年Intel公司推出了首款x87浮点协处理器运算单元(FPU)8087,通过主板上额外的协处理器插槽,安装后不仅可以解决小数的运算问题,并且对于不同的应用,性能提升了20%~500%。
对于计算机发展来说,8087是款非常棒的FPU,但是它的意义真正体现在这款FPU的设计师之一的William Kahan教授设计了IEEE-754标准的雏形,而正是因为这套标准,我们计算机才能精准的处理小数。
1985年时,IEEE推出了IEEE 754-1985标准,随着大佬们的努力,IEEE还推出了目前的版本——IEEE 754-2008。
而我们使用的高级语言中浮点数的运算,如C、C++、JavaScript、Java都是基于这个标准而定。


对于一个非0的二进制数来说,我们可以保证尾数部分一直以1开始,那么我们通过把第一位的1当做隐藏位,默认它的存在,并不把它存入尾数部分,这样就可以提高1位数据表示的精度。
用移码来表示阶码有以下几种好处。
1.方便比较大小和加减
2.保证浮点数的机器零为0
3.特殊值(0和max)比较容易检验
4.提高表示数据的精度


(-1) ^ 符号位 * 1.bbbbb... * 2 ^ 指数部分
下面我们使用4399这个十进制整数我们套用上述公式,反推得到以下64位2进制浮点数结果。
在了解了浮点数表示的标准,与机器数编码标准后,我们来看看计算机中的浮点数是如何运算的。
浮点数运算分为两个部分,阶码运算与尾数运算,并且所有运算均采用补码运算,具体的运算一般可以分为以下五个个步骤,我将试着通过一个浮点数加减法的实际案例结合以下五个步骤进行同步说明:
假设有两组IEEE-754标准的64位双精度浮点数:
第一组x浮点数为: 0 01111111011 1001100110011001100110011001100110011001100110011010
第二组y浮点数为: 0 01111111100 1001100110011001100110011001100110011001100110011010
现在需要进行求x+y的值。
x的阶码为 01111111011
y的阶码为 01111111100
计算 01111111011 - 01111111100
转为补码计算的加法 01111111011 + (-01111111100) = 01111111011 + 10000000100[补]
得出的结果转为原码 11111111111[补] = -1[十]
由于得知x与y的阶差为-1,所以我们需要将小阶x的尾数右移一位
原x的尾数部分1001100110011001100110011001100110011001100110011010
右移后x的尾数11001100110011001100110011001100110011001100110011010
根据0舍1入原则,右移移位后,尾数补上隐藏位的1,舍去移除的0,最后得到x的尾数部分为:
1100110011001100110011001100110011001100110011001101
注意对阶完成后,不管是加减运算,此时原数的阶码已经为大阶的阶码了
如果隐藏位被右移了,那么默认补0
0.1100110011001100110011001100110011001100110011001101
+ 1.1001100110011001100110011001100110011001100110011010
————————————————————————————————————————————————————————
10.0110011001100110011001100110011001100110011001100111
10.0110011001100110011001100110011001100110011001100111 就是尾数计算后的结果。
0 01111111101 0011001100110011001100110011001100110011001100110011(1)
这里计算后1隐藏位还是继续隐藏。
右移后的尾数为上面的数字,这里需要注意的是,最低位被右规了1,造成精度丢失的问题
0011001100110011001100110011001100110011001100110011
+ 0000000000000000000000000000000000000000000000000001
————————————————————————————————————————————————————————
0011001100110011001100110011001100110011001100110100
最终我们得到IEEE-754的浮点数0 01111111101 0011001100110011001100110011001100110011001100110100
将它转换为10进制数得到0.30000000000000004440892098500626
而题目中的x和y是0.1和0.2。
说到这里聪明的你应该知道为什么0.1+0.2不等于0.3了吧。

