初学者须知:Python里的数
2020-04-30
作者:Moshe Zadka
翻译:老齐
与本文相关的图书推荐:《跟老齐学Python:轻松入门》
Python中,数,用各种形式表示,不同形式的数有各自的用途。
整数
整数,令人惊叹于它的简单。两个整数相除,例如4/3
,得到一个浮点数,并且(4/3)*3
的结果也是浮点数4.0
。即便你没有定义浮点数,在进行除法运算的时候,它会自动出现。
浮点数
浮点数不是一般意义的数。按照数学上的规定,数应该遵循如下原则:减法是加法的逆运算,加法结合律,等等。
例如:
1 | >>> 1 + 2 - 2 - 1 |
两个数相加,再分别减去它们,上述居然出现了不同的结果。
它们也不会遵循结合律:a + (b + c) = (a + b) + c
1 | >>> a = 2**-53 |
以上仅仅是浮点数运算中存在的两个“小问题”,还不令你惊讶吗?此处不便将浮点数各种出乎意料的运算一一展现。
分数
很多看似简单的程序,遇到分数,就会出问题,比如运算时间暴增,算法的复杂度加倍。遇到分数的时候,算法时间不是跟输入成正比,而是指数增长。
如果时间足够长,内存爆掉也是常见的。
加法就是其中一个典型例子
1 | >>> print(set(type(p) for p in primes)) |
这段程序,计算了一些素数的倒数的和。在笔记本电脑上,10000个这样的数相加,要1分钟,最终输出结果的大小超过了90K。
对比着,执行浮点数运算,性能更好。
1 | >>> print(set(type(p) for p in primes)) |
这次运行时间小于1毫秒,并且,者还可能是因为用datetime
测量产生的误差,快了10000倍。而且输出结果的大小仅有17比特,下降了1000多倍。然而,计算结果有误差。
1 | Approximate value 2.7092582487972945 |
误差低于 10的-14次方,这就如同将火箭发射月球上偏差了1毫米,用浮点数计算得到的结果足够精确,并且效率更高。
对此,一般的观点是:Python进行分数运算很慢。对此,Python可以承担10倍的责任,但不是10000倍。有一个第三方模块,quicktions,用Cython执行分数的运算。
用quicktions,真的“很快”。在我的笔记本电脑上,上面那个程序的时间,从1分16秒,缩短到1分15秒。
问题在于程序本身,在程序中,我精心选择了一种输入方案,以素数作为分母进行分数相加,这本来就是一种很坏的情况。
小数
小数在财务中用途最广,最无聊的是居然以法律的方式规定了小数的形式。然而,Python中所有的小数点运算,都有上下文精确度问题,对此,可以用专门的模块解决。
1 | >>> getcontext().prec = 6 |
在实际项目中,代码中设置精度的位置和进行计算的位置可能间隔几百行,计算可以在一个函数中,也可以在另外一个文件。
最安全的方法是使用localcontext
:
1 | >>> getcontext().prec = 6 |
只要你认真地用localcontext
,小数运算不会出问题。
总结
你在程序中用到数字的时候,是否想过:应该用什么类型?会发生什么?误差重要吗?
什么也不想,会意味着暗藏bug。
原文链接:https://orbifold.xyz/numbers.html
搜索技术问答的公众号:老齐教室
在公众号中回复:老齐,可查看所有文章、书籍、课程。
觉得好看,就点这里👇👇👇
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
关注微信公众号,读文章、听课程,提升技能