1 .希望发现问题并获得长度为57的、从0开始、间隔为0.01的数组。 当然想按以下方式进行编码,但结果出乎意料:
importnumpyasnpt1=NP.arange (0,0.01 * 57,0.01 ) T1.shape ) 58L,)但是,如果用同样的方法可以得到正确的长度58的从0开始的间隔0.01的数组,结果是
T2=NP.arange (0,0.01 * 58,0.01 ) T2.shape ) 58L,)到底是什么问题?
2 .浮点数的精度问题我找了一些资料,但是很混乱。 为了理解为什么会出现上述问题,通过总结整理,列举几个重要内容。
)1)在计算机中用二进制表示小数的python的浮点型(float ),和c语言的double型)很相似,实际上应该是一样的,但毕竟标准的python是用c写的。 )是双精度浮点类型,可以用原样的十进制数或科学计数法表示。 每个双精度浮点数占8字节(64位),完全符合IEEE754号标准(52M/11E/1S )。 也就是说,其中52位表示底数,11位表示指数,剩下的1位表示符号。 虽然看起来相当完美,但实际精度取决于机器体系结构和创建python解释器的编译器。
试着用二进制形式表示0.1。 通过以下计算,可以看出0.1在十进制中是有限小数,而在二进制中是无限循环。 (实际上,0.0~0.9这10个小数中,只有0.0和0.5在二进制中是有限的。 ) :
根据IEEE754标准,有效位数只能表示52位。 在python中,0.1实际上是这样表示的。 (python不提供. bin函数,只是. hex函数) )。
(0.1 )、hex )、)0x1.9999999999AP-4 )0x是十六进制数,p-4表示小数点向左移动4位。 IEEE754标准的52位在此不包含小数点前的1,因此展开’0x1.999999999999ap-4 ‘时,将显示:
#原表示: 0.000110011001100110011001100110011001100110011001# ie001100110011001100011000110001100001110000
python
print’%.20f’%(0.1 ) 0.100000000000000000555 print ‘ %.20f ‘ % ) 0.1.1 ) 0.20000000000001110print’%.20f ) int’%.20f’%(0.10.2 ) 0.300000000000000441 print ‘ %.20f ‘ % (0.1 *3) 0.3000000000000
d : (developvitest.CPP # include iostream # includeiomanipusingnamespacestd; int main () { double a=0.1,b=0.2,c=0.3; int d=3; cout’0.1=’setprecision(20 ) aendl; cout’0.10.1=’setprecision(20 ) a aendl; cout’0.2=’setprecision(20 ) bendl; cout’0.10.2=’setprecision(20 ) a bendl; cout’0.1*3=’setprecision(20 ) a*dendl; cout’0.3=’setprecision(20 ) cendl; } : wqd :\developgt est.CPP-o test.exe.\ test.exe 0.1=0.100000000000550.1=0.200
00000000000001110.2 = 0.20000000000000001110.1+0.2 = 0.300000000000000044410.1*3 = 0.300000000000000044410.3 = 0.2999999999999999889
可以看到,C++和python的情况是一致的。
在编程语言上的0.3实际是比数学上的0.3要小一点点的;而0.1+0.2或0.1*3运算,在编程语言上的误差会跟着进行运算,运算结果会积累误差造成其要比数学上的0.3要大一点点。
解析numpy.arange使用浮点数的问题
根据以上的分析,就能解释使用numpy.arange所出现的现象了,我们可以验证一下:
>>> import numpy as np>>> np.arange(0, 0.1*3, 0.1)array([ 0. , 0.1, 0.2, 0.3])>>> np.arange(0, 0.3, 0.1)array([ 0. , 0.1, 0.2])>>>>>> print ‘%.20f’%(0.57)0.56999999999999995115>>> print ‘%.20f’%(0.01*57)0.57000000000000006217>>> print ‘%.20f’%(0.01*58)0.57999999999999996003 也就是说,第二个表示终值的参数如果使用0.1*3,其实是使用了一个稍微比0.3大一点点的数,那么根据numpy.arange的使用规则(产生的数组是不包含终值的),就会包含到0.3(但实际上是比0.3小一点点的数)。而直接使用0.3作为终值,由于其实际上是比0.3小一点点的数,因此得到的数组就不会包含0.3。同理,0.01*57是比0.57稍微大一点点的数,所以产生的数组自然就包含了0.57(但实际上是比0.57小一点点的数),就得不到期望的数组了
不过讲道理,后来我才发现人家写numpy.arange的时候已经说了如果是用非整数作为step,最好使用numpy.linspace(不过似乎我遇到的是stop的问题)
>>> help(np.arange)…arange(…)arange([start,] stop[, step,], dtype=None)…When using a non-integer step, such as 0.1, the results will often notbe consistent. It is better to use “linspace“ for these cases. 3.解决方案 (1)解决numpy.arange问题的方案
尽量避免使用arange!!可以使用xrange:
>>> import numpy as np>>> t1 = np.array([x*0.01 for x in xrange(57)])>>> t1.shape(57L,)>>> t2 = np.array([x*0.01 for x in xrange(58)])>>> t2.shape(58L,) (2)解决十进制浮点数精度问题的方案:使用decimal
使用decimal模块中的Decimal类:
>>> from decimal import Decimal>>> d1 = Decimal(.1)>>> d2 = Decimal(“.1”)>>> d3 = Decimal(“1.0”)>>> d1Decimal(‘0.1000000000000000055511151231257827021181583404541015625’)>>> d2Decimal(‘0.1’)>>> d3Decimal(‘1.0’)>>> d1+1.0Traceback (most recent call last): File “<stdin>”, line 1, in <module>TypeError: unsupported operand type(s) for +: ‘Decimal’ and ‘float’>>> d1+d3Decimal(‘1.100000000000000005551115123’)>>> d2+d3Decimal(‘1.1’)>>> print d2+d31.1
参考书: 《Python核心编程(第二版)》,人民邮电出版社
python版本: 2.7.14 (64bit)