Python中的取整方式

2018/01/19

Categories: Python Tags: PythonBasics

方式一:round() 四舍五入

Python中的 round() 有两个参数,第一个参数是需要处理的数,第二个参数是数位精度,默认为0。

round(3.4)
## 3
round(3.5)
## 4

而有时候会出现奇怪的情况,比如:

round(3.24, 1) #是四舍五入
## 3.2
round(3.26, 1) #是四舍五入
## 3.3
round(3.25, 1) #不是四舍五入
## 3.2
###################################
round(0.44, 1) #是四舍五入
## 0.4
round(0.46, 1) #是四舍五入
## 0.5
round(0.45, 1) #是四舍五入
## 0.5

很多人说Python3中采用的是【四舍六入五留双】,上面的例子说明这种说法是不正确的。其实是因为:

  1. 十进制小数在计算机内是通过二进制小数来近似,在舍和进两个选项中选择更接近的一个
  2. 而当舍和进的两个选项十分接近时,round 选择偶数的选项

这就导致出现的结果非常复杂了。

进一步解释:十进制小数 $0.2$$0.3$ 的二进制表示分别为:

$$ \begin{align} (0.2)_{10} & = \left(\frac{1}{8}+\frac{1}{16}\right)+\left(\frac{1}{128}+\frac{1}{256}\right)+\cdots =\frac{\frac{1}{8}+\frac{1}{16}}{1-\frac{1}{16}} =\frac{3}{15}=\frac{1}{5}\newline &=(0.\dot{0}\dot{0}\dot{1}\dot{1})_2 \end{align} $$

以及 $$ \begin{align} (0.3)_{10} & = \frac{1}{4}+\frac{1}{16}+\frac{1}{64}\cdots =\frac{\frac{1}{4}}{1-\frac{1}{4}} =\frac{1}{3}\newline &=(0.\dot{0}\dot{1})_2 \end{align} $$ 照第一条规则,由于存在截断,$0.2$$0.3$ 实际存储的数均小于本身,理应 $0.3$ 更接近 $0.25$。然而可能由于两者和 $0.25$ 的差距都非常小,根据第二条规则选择了前者。

因此实际中使用 round 会遇到不少bug。

在Python中对小数进行四则运算可以考虑使用 decimal 模块。

from decimal import Decimal
0.3 - 0.25
## 0.04999999999999999
Decimal(0.3) - Decimal(0.25)
## Decimal('0.04999999999999998889776975375')
Decimal("0.3") - Decimal("0.25")
## Decimal('0.05')

方式二: math.ceil() 向上保留

math 模块的 ceil 函数返回大于等于输入值的最小整数,注意输入值为正或负的情形:

from math import ceil
ceil(2.1)
# 3
ceil(-2.1)
# -2

方式三: math.floor() 向下保留

math 模块的 floor 函数返回小于等于输入值的最大整数,注意输入值为正或负的情形:

from math import floor
floor(2.1)
# 2
floor(-2.1)
# -3

方式四:int() 保留整数部分

int 纯粹返回整数部分:

int(2.1)
# 2
int(-2.1)
# -2

也就是说 $$ \text{int}(x)= \begin{cases} \text{math.floor}(x),&x\geq 0\newline \text{math.ceil}(x),&x<0 \end{cases} $$

方法五:使用 numpy 模块

numpy 模块中包含上述所有函数,使用该模块最大的好处就是可以作用于数组上!

  import numpy as np
  np.round([2.25, -2.25], 1)
  ## array([ 2.2, -2.2])
  np.ceil([-2.2, 2.2])
  ## array([-2.,  3.])
  np.floor([-2.2, 2.2])
  ## array([-3.,  2.])
  np.trunc([-2.2, 2.2])
  ## array([-2.,  2.])
  np.int_([-2.2, 2.2])
  ## array([-2,  2])
  np.rint([-1.45, 1.45])
  ## array([-1.,  1.])
  np.int_(np.round(([-1.45, 1.45])))
  ## array([-1,  1])