float计算丢精度_科学计算基础(2)——计算机算数(Computer Arithmetic)

8cec083522616f924b965c8509419e79.png

要了解科学计算,首先要知道数据是如何在计算机中存储和表达的。在计算机基础中我们知道,所有的数据在计算机内存中都是以二进制数的形式存储的,但对于不同的数据类型,二进制数所代表的意义也不尽相同。下面我们来看两种最常见的数据类型:整数和浮点数。

1. 整数

很自然的,对于一个给定的十进制整数,可以将它转换为二进制数,从而在计算机中表示。 下图中的8位(8 bits)二进制数,

4b84920d581d4b19babc670218c3fc19.png

表示

从Python3.0起,可以表示的整数的最大值上限被移除[1],这就意味着我们可以精确表示任何整数,也就是说只要将给定的整数转换为二进制数,然后占用相应长度的内存空间即可。理论上16G的内存可以存储的最大整数约为

另外,由于两个不同整数之间的最小间隔为1(整数的机器精度),因此,与整数有关的加、减和乘法都可以被精确计算,并且没有任何舍入误差。

例子1: 大整数及其运算

注1:普通除法运算 / 在Python中会默认转换为浮点数,因此并不能保证完全精确。

注2:整数除法//可以保证精确,但是结果只有商,余数可以用% 求得。

a = 135791113151719
b = 246810
print(f'a={a}, b={b}')
print(f'a+b={a+b}')
print(f'a-b={a-b}')
print(f'axb={a*b}')
print(f'a÷b={a//b}......{a%b}')

输出:

a=135791113151719, b=246810 
a+b=135791113398529 
a-b=135791112904909 
axb=33514604636975766390 
a÷b=550184810......195619

2. 浮点数

不同于整数,实数是连续的,这就意味着两个不相同的实数之间的最小距离可以无限接近于0。 前文中对于整数的存储方式显然不再适用,我们并不能把所有实数都在计算机中表示出来。 甚至我们都不能把0到1之间的所有实数表达出来。

这就要求计算机对实数运算进行一定的近似,使得给定任意一个实数,我们都能找到一个与它接近的计算机浮点数表达。同时,计算机浮点数系统需要保证运算精度速度。这就要求计算机浮点数系统需要尽可能满足下面的条件:

  1. 每个浮点数占用的内存空间一致。
  2. 覆盖的范围尽可能大
  3. 舍入误差或机器精度尽可能的小。关于舍入误差,参见我之前的文章
Gong Cheng:科学计算基础(1)——误差​zhuanlan.zhihu.com
93367318e611a5e3053066ae55409da5.png

下面我们会一步一步的设计一个8位(8 bits)的浮点数系统,也就是用8位的256个二进制数字来表达浮点数,并逐步满足上述条件。 从而探究一下真正的IEEE浮点数标准是如何设计和指定的。 整个的这个设计过程,也可以在我的网络课件中找到:

2.1 简单的二进制表达浮点数

类似二进制以2为基的方式,我们也可以用

为基,那么下图的这个8位二进制数就表示

4b84920d581d4b19babc670218c3fc19.png

事实上,这个8位二进制数和前面整数一章用到的是同一个二进制数,但他们因为数据类型不同,代表的含义也就不同了。

由此,我们的8位浮点数系统可以表示256个在0到

之间的实数,它们的机器精度为

小结此方法的机器精度是8位系统可达到的最小值,但覆盖范围仅为0到1

2.2 扩大范围

想要表达更大的数,可以从上面的方式出发,将得到的0到1之间的数都乘以一个固定的值,例如乘以8。 这样,我们的浮点数系统的覆盖范围扩大到了0到7.96875。 但是这个操作也会将机器精度乘以相应的值,使其下降为了

小结等比例的扩大范围会大幅降低机器精度

2.3 标准化

延续上面的思路,我们可以用乘以多个不同的倍数的方法来控制所扩大的范围。 也就是说,从8位二进制中拿出2位(下面的例子中用的是最后两位,但也可以是前面两位),用来记录扩大的倍数,而前面的6位同之前一样用作表达0到1之间的实数,这6位被称作尾数位(mantissa)。

为了进一步扩大范围,取出的2位从二进制转化为十进制后,被放在以2为基(basis)的指数位置上形成放大的倍数,因此这2位在这个系统中被称为指数位(exponent)。

如下图,我们继续使用同样的8位二进制数,在经过标准化过程后,表达的数为

420884bc0655c272a525078cf9ba41f4.png

对于我们的8位浮点数系统而言,相当于将所有表达在0到7.875之间浮点数标准化,机器精度随着浮点数增大而等比例增大。 即在任意取值情况下,相对的机器精度都保持在

小结此方法在保持了与2.2相似的范围的同时,将机器精度提升了一倍

但是,我们注意到,这个方法有一个严重的问题在于重复,例如01011101和10111000都表示了同一个数。

2.4 去掉重复

去掉重复数字的方法很巧妙,这个方法并不改变2.3中的标准化方式,只是在对尾数位求和过程中加上一个常数1,即

使用这种改进方法,我们可以表达1至15.875之间的256个不同的实数, 我们把这些浮点数画在数轴上,如下图

c44bd8cb6c0e8746cc3fd798c3c8b41d.png

可以发现,这样表达的浮点数保持了各处一致的相对机器精度,这个相对值约为

:数轴上小于1的范围称为下溢(underflow),而超过15.875的范围称为上溢(overflow)。

2.5 更多的

下面的几个步骤可以进一步完善我们的浮点数系统:

  • 为了表达负数,我们需要从尾数位(mantissa)中取出一位,作为符号位
  • 为了将下溢降低,我们将指数位(exponent)表达的十进制数(0,1,2,3)向负数平移,变成(-1,0,1,2)。

3. IEEE 浮点数标准

现在的计算机系统采用的是

754-2019 - IEEE Standard for Floating-Point Arithmetic - IEEE Standard​ieeexplore.ieee.org

每一个浮点数(Python中的float类型)占用64位。如下图所示,其中第1位为符号位,下面11位为指数位(exponent),最后52位为尾数位(mantissa)。

c8d94b18a61bfddf23740af4c711bf79.png

可知

。指数位
的标准范围定为
,把
作为特殊位,并把
作为保留位(我们放在最后分析)。

由此,我们可以求得:

  • 机器精度eps
  • 最小的正浮点数realmin
    ,在python中可以通过
    numpy.finfo(float).tiny获得。
  • 最大的浮点数realmax
    ,在python中可以通过
    numpy.finfo(float).max获得。
  • ,表示无穷大
    ,即任何大于
    realmax的数,在python中可以通过numpy.inf得到。
  • ,则表示这不是一个数(NaN,Not a Number)。通常出作为
    0/0或者numpy.inf-numpy.inf的结果。我们也可以通过numpy.nan表达它。

例子2: numpy中查看浮点数标准

import numpy as np

print(f'minimal positive normal float: {np.finfo(float).tiny}')
print(f'maximal float: {np.finfo(float).max}')
print(f'machine epsilon: {np.finfo(float).eps}')
print(f'infinity: {np.inf}')
print(f'not a number: {np.nan}')

输出

minimal positive normal float: 2.2250738585072014e-308 
maximal float: 1.7976931348623157e+308 
machine epsilon: 2.220446049250313e-16 
infinity: inf 
not a number: nan

3.1 亚标准(subnormal)

在很多计算系统中,除了上面的标准浮点数以外,还存在着亚标准浮点数(subnormal floating point numbers)。

它们是比最小的标准浮点数realmin

)还要小的正实数。

我们先来看一下下面这个例子:

import numpy as np

realmin = np.finfo(float).tiny
for i in range(17):
    print(f'realmin x 10^{-(i+1)} = {realmin*(10**(-i-1))}')

输出

realmin x 10^-1 = 2.225073858507203e-309
realmin x 10^-2 = 2.2250738585072e-310
realmin x 10^-3 = 2.225073858507e-311
realmin x 10^-4 = 2.225073858507e-312
realmin x 10^-5 = 2.2250738585e-313
realmin x 10^-6 = 2.2250738583e-314
realmin x 10^-7 = 2.22507386e-315
realmin x 10^-8 = 2.22507384e-316
realmin x 10^-9 = 2.225074e-317
realmin x 10^-10 = 2.225074e-318
realmin x 10^-11 = 2.22507e-319
realmin x 10^-12 = 2.2253e-320
realmin x 10^-13 = 2.223e-321
realmin x 10^-14 = 2.2e-322
realmin x 10^-15 = 2.5e-323
realmin x 10^-16 = 0.0
realmin x 10^-17 = 0.0

可以看到

  • 我们能够表示比最小标准数realmin更小的一些正实数;
  • 它们的精度随着我们远离realmin而降低;
  • 最终,当这个数与realmin的比小于约
    时,它变成了0。

这是因为,我们是用保留位

来表示这类亚标准浮点数,它们范围为
realmin
eps到realmin(eps为机器精度)。也就是说,我们能表示的最最小的正实数为
,但这个数的精度非常低。

:这里尽管

,但我们并不是用标准浮点数的转换规则进行运算的,同时在尾数前面加的1也并没有在这出现。

参考

  1. ^python3 int https://docs.python.org/3.1/whatsnew/3.0.html#integers
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值