0%

[经验总结]取整与取余

最近开始复习、深挖 Python 基础知识,有机会深入探索一些以前没有想过的事情。

我们知道,Python 内置函数 intround 可以把一个浮点数取整,比如

1
2
3
4
>>> int(1.1)
1
>>> round(1.1)
1

它们是如何工作的呢?

int 函数

1
int(x, base=10)

本文中我们不关注一个数的进制,统一按十进制处理。我们来看一个例子:

1
2
3
4
print(int(1.1)) # 1
print(int(1.9)) # 1
print(int(-1.1)) # -1
print(int(-1.9)) # -1

看起来 int 函数就是简单地截取小数点前面的数值。

round 函数

1
round(x, ndigits=None)

这里 round 的处理近似于四舍五入,我们先看几个没有争议的例子:

1
2
3
4
print(round(1.1)) # 1
print(round(1.9)) # 2
print(round(-1.1)) # -1
print(round(-1.9)) # -2

现在有意思的时候到了,.5 应该如何进位呢?

1
2
3
4
print(round(1.5)) # 2
print(round(2.5)) # 2
print(round(-1.5)) # -2
print(round(-2.5)) # -2

根据官方文档,rounding 会选择被 2 整除的数,所以 1.5 和 2.5 的 rounding 都是 2。当 ndigits 取非 0 值时,这个原则仍然适用。

1
2
print(round(1.55, 1)) # 1.6
print(round(1.45, 1)) # 1.4

上述例子中的 ndigits 在为 0 时为 None,取 0 会有什么变化吗?

1
2
round(1.1, ndigits=None) # 1
round(1.1, 0) # 1.0

是有变化的!如果 ndigits=None 或者干脆省略,返回一个整数;如果 ndigits=0,返回一个带有一位小数点的整数。无独有偶,numpyTensorFlowPyTorch 也有 numpy.roundtensorflow.math.roundtorch.round 与 Python 原生 round 函数对应,它们与原生函数有什么区别呢?

1
2
3
4
5
6
7
8
print(np.round(1.5)) # 2.0
print(np.round(2.5)) # 2.0

print(tf.round(tf.Variable(1.5)).numpy()) # 2.0
print(tf.round(tf.Variable(1.5)).numpy()) # 2.0

print(torch.round(torch.tensor(1.5)).item()) # 2.0
print(torch.round(torch.tensor(2.5)).item()) # 2.0

可以看到,numpy,TensorFlow 和 PyTorch 的 round 函数的工作原理与 Python 原生函数相同。顺便提一句,TensorFlow 和 PyTorch 的 round 函数只能取整,返回一个带有一位小数点的整数;numpy 的 round 函数与 Python 原生函数相同,但是变量名不是 nsdigits 而是 decimals

Python 里的相除取整(//)与相除取余(%

所谓的相除取整和相除取余很好理解,就是一个除法如果不能整除的话就分别取整除部分和余数部分:

1
2
print(123 // 10) # 12
print(123 % 10) # 3

本来很简单的一件小事遇到负数就有意思了:

1
2
3
4
5
6
print(-123 // 10) # -13
print(123 // -10) # -13
print(-123 // -10) # 12
print(-123 % 10) # 7
print(123 % -10) # -7
print(-123 % -10) # -3

这是怎么回事呢?

相除取整

我们把几个除法的结果比较一下:

1
2
3
4
5
6
7
8
9
print(123 / 10) # 12.3
print(123 // 10) # 12
print(int(123 / 10)) # 12
print(-123 / 10) # -12.3
print(-123 // 10) # -13
print(int(-123 / 10)) # -12
print(-123 / -10) # 12.3
print(-123 // -10) # 12
print(int(-123 / -10)) # 12

在没有看函数源代码的情况下,我们可以大概说,// 操作为向下取整。如果想要一个负结果的向上取整的结果,可以使用 int 配合普通除法。负数除以一个负数与正数除以一个正数的结果相同。

相除取余

还是比较几个除法取余的结果:

1
2
3
4
print(123 % 10) # 3
print(123 % -10) # -7
print(-123 % 10) # 7
print(-123 % -10) # -3

其实在 Python 中,取余的计算公式与别的语言并没有什么区别:
$$r = a - n * [a // n]$$
其中 r 是余数,a 是被除数,n 是除数。不过在 a // n 这一步,当 a 是负数的时候,上面提到会向下取整,所以有:
$$-123 % 10 = -123 - 10 * (-123 // 10) = -123 - 10 * (-13) = 7$$
其余的两个相除取余也可以按照此法推演出来。

欢迎关注我的其它发布渠道