python decimal转float_来聊聊Python里的“数”

Python中,数,用各种形式表示,不同形式的数有各自的用途。

整数

整数,令人惊叹于它的简单。两个整数相除,例如4/3,得到一个浮点数,并且(4/3)*3的结果也是浮点数4.0。即便你没有定义浮点数,在进行除法运算的时候,它会自动出现。

浮点数

浮点数不是一般意义的数。按照数学上的规定,数应该遵循如下原则:减法是加法的逆运算,加法结合律,等等。

例如:

>>> 1 + 2 - 2 - 10>>> 0.1 + 0.2 - 0.2 - 0.12.7755575615628914e-17

两个数相加,再分别减去它们,上述居然出现了不同的结果。

它们也不会遵循结合律:a + (b + c) = (a + b) + c

>>> a = 2**-53>>> (a + a) + 1 == a + (a + 1)False

以上仅仅是浮点数运算中存在的两个“小问题”,还不令你惊讶吗?此处不便将浮点数各种出乎意料的运算一一展现。

分数

很多看似简单的程序,遇到分数,就会出问题,比如运算时间暴增,算法的复杂度加倍。遇到分数的时候,算法时间不是跟输入成正比,而是指数增长。

如果时间足够长,内存爆掉也是常见的。

加法就是其中一个典型例子

>>> print(set(type(p) for p in primes))>>> one = fractions.Fraction(1)>>> before = datetime.now()>>> res = sum(one/p for p in primes[:10000])>>> after = datetime.now()>>> print("It took", after-before)>>> print("Size of output", len(str(res)))>>> print("Approximate value", float(res)){}It took 0:01:16.033260Size of output 90676Approximate value 2.7092582487972945

这段程序,计算了一些素数的倒数的和。在笔记本电脑上,10000个这样的数相加,要1分钟,最终输出结果的大小超过了90K。

对比着,执行浮点数运算,性能更好。

>>> print(set(type(p) for p in primes))>>> before = datetime.now()>>> res = sum(1/p for p in primes[:10000])>>> after = datetime.now()>>> print("It took", after-before)>>> print("Size of output", len(str(res)))>>> print("Approximate value", float(res)){}It took 0:00:00.000480Size of output 17Approximate value 2.709258248797317

这次运行时间小于1毫秒,并且,者还可能是因为用datetime测量产生的误差,快了10000倍。而且输出结果的大小仅有17比特,下降了1000多倍。然而,计算结果有误差。

Approximate value 2.7092582487972945Approximate value 2.709258248797317                    1234567891234

误差低于 10的-14次方,这就如同将火箭发射月球上偏差了1毫米,用浮点数计算得到的结果足够精确,并且效率更高。

对此,一般的观点是:Python进行分数运算很慢。对此,Python可以承担10倍的责任,但不是10000倍。有一个第三方模块,quicktions,用Cython执行分数的运算。

用quicktions,真的“很快”。在我的笔记本电脑上,上面那个程序的时间,从1分16秒,缩短到1分15秒。

问题在于程序本身,在程序中,我精心选择了一种输入方案,以素数作为分母进行分数相加,这本来就是一种很坏的情况。

小数

小数在财务中用途最广,最无聊的是居然以法律的方式规定了小数的形式。然而,Python中所有的小数点运算,都有上下文精确度问题,对此,可以用专门的模块解决。

>>> getcontext().prec = 6>>> Decimal(1) / Decimal(7)Decimal('0.142857')>>> getcontext().prec = 28>>> Decimal(1) / Decimal(7)Decimal('0.1428571428571428571428571429')

在实际项目中,代码中设置精度的位置和进行计算的位置可能间隔几百行,计算可以在一个函数中,也可以在另外一个文件。

最安全的方法是使用localcontext:

>>> getcontext().prec = 6>>> # 6853 lines elided... with localcontext() as ctx:...     ctx.prec = 10...     Decimal(1) / Decimal(7)...Decimal('0.1428571429')

只要你认真地用localcontext,小数运算不会出问题。

总结

你在程序中用到数字的时候,是否想过:应该用什么类型?会发生什么?误差重要吗?

什么也不想,会意味着暗藏bug。

4bed4be639e1881e90df7fc18bcab1d4.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值