python中运算过程中小数精度的控制

用python进行运算时,如果会出现浮点数,有时候会需要对小数精度进行控制,主要方法有以下几种:

1、round()

round()是python的内置方法, round()如果只有一个数作为参数,不指定位数的时候,返回的是一个整数,而且是最靠近的偶数整数(python2.7版本则是四舍五入,这个针对的是python3版本)。如果有两个参数则最多保留精度为第二个参数大小。

In [1]: 4 - 3.6
Out[1]: 0.3999999999999999

In [2]: round(4 - 3.6)
Out[2]: 0.0

In [3]: round(4-3.6,2)
Out[3]: 0.4

In [4]: round(4-3.6,5)
Out[4]: 0.4

In [5]: round(2.55555,3)
Out[5]: 2.556

In [6]: round(2.555,30)
Out[6]: 2.555



In [7]: round(2.5)  #注意这个从python3开始是向偶数看齐,即整数部分为偶数时舍弃小数部分,为奇数时进一位
Out[7]: 3.0  #python3输出为2

In [8]: round(3.5)
Out[8]: 4.0

python文档说明如下:
round(number[, ndigits])

Return the floating point value number rounded to ndigits digits after the decimal point. If ndigits is omitted, it defaults to zero. The result is a floating point number. Values are rounded to the closest multiple of 10 to the power minus ndigits; if two multiples are equally close, rounding is done away from 0 (so, for example, round(0.5) is 1.0 and round(-0.5) is -1.0).

Note

The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float. See Floating Point Arithmetic: Issues and Limitations for more information.

注意:round第二个参数可以为负数,这是用在整数位四舍五入的时候:

In [222]: round(12345, -1)
Out[222]: 12340

In [223]: round(12345, -2)
Out[223]: 12300

In [224]: round(12345, -3)
Out[224]: 12000

In [225]: round(15689, -4)
Out[225]: 20000

In [226]: round(15689, -3)
Out[226]: 16000

In [227]: round(15689.3, -3)
Out[227]: 16000.0

2、使用格式化

In [9]: '%.2f'%3.1415926
Out[9]: '3.14'

In [11]: float('%.3f'%1.234567890)
Out[11]: 1.235
Python默认的是17位精度,也就是小数点后16位,但是这里有一个问题,就是当我们的计算需要使用更高的精度(超过16位小数)的时候该怎么做呢?
方法1:继续像上面使用格式化
In [12]: '%.30f'%(1.0/3)
Out[12]: '0.333333333333333314829616256247'
方法2:使用decimal模块,配合使用其中的getcontext
In [1]: from decimal import Decimal, getcontext

In [2]: Decimal(1) / Decimal(3)
Out[2]: Decimal('0.3333333333333333333333333333')

In [3]: getcontext().prec = 10

In [4]: Decimal(1) / Decimal(3)
Out[4]: Decimal('0.3333333333')

In [5]: len(str(_))
Out[5]: 12  #有2个是前面的0和小数点,所以恰好是10In [6]: print getcontext()
Context(prec=10, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999, capitals=1, flags=[Inexact, Rounded], traps=[DivisionByZero, Overflow, InvalidOperation])
In [239]: from decimal import localcontext

In [240]: a = Decimal('1.3')

In [241]: b = Decimal('1.7')

In [242]: a / b
Out[242]: Decimal('0.7647058823529411764705882353')

In [243]: print (a/b)
0.7647058823529411764705882353

In [244]: with localcontext() as ctx:
     ...:     ctx.prec = 3
     ...:     print (a/b)
     ...:     
0.765

In [245]: with localcontext() as ctx:
     ...:     ctx.prec = 50
     ...:     print (a/b)
     ...:     
0.76470588235294117647058823529411764705882352941176

具体见:https://docs.python.org/2.7/library/decimal.html#module-decimal

另:注意下这种情况:

In [247]: nums = [1.23e+18, 1, -1.23e+18]

In [248]: sum(nums)  #注意这个,输出根本不是预想中的那样
Out[248]: 0.0

此时需要math模块:

In [254]: import math

In [255]: math.fsum(nums)
Out[255]: 1.0
In [256]: math.fsum?
Docstring:
fsum(iterable)

Return an accurate floating point sum of values in the iterable.
Assumes IEEE-754 floating point arithmetic.
Type:      builtin_function_or_method

原理详解部分:

我们知道,将一个小数转化为二进制表示的方式是,不断的乘2,取其中的整数部分。例如


(1) 0.625*2 = 1.25, 整数部分为1,小数部分为0.25
(2) 0.25 * 2 = 0.5 , 整数部分为0,小数部分为0.5
(3) 0.5 * 2 = 1 , 整数部分为1,小数部分为0


所以0.625的二进制表示就是0.101。

然而有些小数,例如0.4,并不能够精确的转化为二进制表示,用上面的这种方法计算:


(1) 0.4*2=0.8 整数部分为0,小数部分为0.8
(2) 0.8*2=1.6 整数部分为1,小数部分为0.6
(3) 0.6*2=1.2 整数部分为1,小数部分为0.2
(4) 0.2*2=0.4 整数部分为0,小数部分为0.4

(5) 0.4*2=0.8 整数部分为0,小数部分为0.8
(6) 0.8*2=1.6 整数部分为1,小数部分为0.6
(7) 0.6*2=1.2 整数部分为1,小数部分为0.2
……


所以0.4转化为二进制,应该是0.0110… 这样一个无限循环小数。

计算机的内存、cpu寄存器等等这些硬件单元都是有限的,只能表示有限位数的二进制位,因此存储的二进制小数就会和实际转换而成的二进制数有一定的误差。(你可以试着将0.3转化为二进制表示,也将出现一个循环小数。)

实际上,大多数情况下,小数在计算机中是以一种类似科学计数法的形式表示的,具体的可以参考一下其他的资料。但即便如此,仍然存在误差。

浮点数是用机器上浮点数的本机双精度(64 bit)表示的。提供大约17位的精度和范围从-308到308的指数。和C语言里面的double类型相同。Python不支持32bit的单精度浮点数。所以在python中不建议直接将两个浮点数进行大小比较,或者做精确的计算,往往会得到意想不到的结果。当然,如果非要用,可以参考decimal模块的相关内容。如果程序需要精确控制区间和数字精度,可以考虑使用numpy扩展库。
另外记录个链接:http://justjavac.com/codepuzzle/2012/11/11/codepuzzle-float-who-stole-your-accuracy.html

参考:https://www.zhihu.com/question/25457573/answer/30851062

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值