我正在学习 Go,并且我有 Python 背景。
最近,我偶然发现了
%
(模)运算符的行为,它与 Python 中相应的运算符不同。与模运算和余数的定义完全相反,负整数与正整数的模返回负值。
示例:
a, b, n = -5, 5, 3
for i in range(a, b):
print(i%n)
输出:
1
2
0
1
2
0
1
2
0
1
a, b, n := -5, 5, 3
for i:=a; i<b; i++ {
fmt.Println(i%n)
}
输出:
-2
-1
0
-2
-1
0
1
2
0
1
在阅读了模运算符和一些类似的问题询问这些差异背后的原因后,我了解到这些差异是由于相关语言的设计目标造成的。
Go 中是否有内置功能可以复制 Python 的模数运算?
替代:是否有一种内部方法来计算“模数”而不是“余数”?
参见 一位语言设计者的评论:
目前的定义有几个原因:
- 由于 x86 架构,% 的当前语义可直接使用
- 改变初等运算符 % 的含义而不改变其名称会令人困惑
- 从 % 结果计算另一个模数是相当容易的
请注意,% 计算“余数”而不是“模数”。
标准库中没有复制Python模数运算的运算符或函数。
可以编写一个函数来复制 Python 的模数运算:
func modLikePython(d, m int) int {
var res int = d % m
if ((res < 0 && m > 0) || (res > 0 && m < 0)) {
return res + m
}
return res
}
请注意,在 Python 中
5 % -3
是 -1
,此代码也复制了该行为。如果您不想这样做,请删除 ||
语句中 if
之后的第二部分。
如果您想要一个实现 Python 求模行为的 Go 函数,请尝试以下操作:
func mod(a, b int) int {
return (a % b + b) % b
}
如果您想了解模数与余数的解释,请继续阅读。
几乎所有模数的定义都是除法后的余数。换句话说,模与余数是错误的二分法,因为它们是同一件事。
之所以出现区别,是因为定义余数的合理方法不止一种,不同的定义会给出不同的结果。
例如,考虑 11 除以 5,真商为 2.2。在整数的上下文中,有不止一种方法可以解释这一点:
对于负 11 除以 5:
有几种方法可以指定我们想要的余数。例如,通过选择如何舍入真商来找到整数商(向上舍入、向下舍入、向零舍入)。也许最简单的方法是选择我们希望余数具有什么符号(当它非零时)。例如,我们可以说余数
a mod b
应该与 a
具有相同的符号。这就是 Go 的 %
运算符的工作原理。您也可以说余数 a mod b
应该与 b
具有相同的符号。这就是 Python 的 %
运算符的工作原理。
math/big
欧几里得模数:
package main
import "math/big"
func mod(x, y int64) int64 {
bx, by := big.NewInt(x), big.NewInt(y)
return new(big.Int).Mod(bx, by).Int64()
}
func main() {
z := mod(-5, 3)
println(z == 1)
}
在第二季度,您可以使用:
func modNeg(v, m int) int {
return (v%m + m) % m
}
会输出:
modNeg(-1, 5) => 4
modNeg(-2, 3) => 0
在大多数情况下,只需将第二个数字添加到结果中即可:
Python:
-8%6 => 4
戈兰:
-8%6 + 6 => 4
所以函数会是这样的:
func PyMod(d int, m int) int {
d %= m
if d < 0 {
d += m
}
return d
}
除了
a%-b
之外,它还适用于其他一些情况,例如 -a%b
。
但是如果你想让它对
-a%-b
也起作用,请这样做:
func PyMod(d int, m int) int {
// Add this condition at the top
if d < 0 && m < 0 {
return d % m
}
d %= m
if d < 0 {
d += m
}
return d
}