C和Python - 模数(%)操作的不同行为

psi*_*lia 63 c python math modulo

我发现相同的mod操作会产生不同的结果,具体取决于使用的语言.

在Python中:

-1 % 10
Run Code Online (Sandbox Code Playgroud)

产生9

在C中它产生-1!

  1. 哪一个是正确的模数?
  2. 如何使C中的mod操作与Python中的相同?

Ale*_*x B 69

  1. 两种变体都是正确的,但在数学(特别是数论)中,Python的模数是最常用的.
  2. 在C中,您((n % M) + M) % M可以获得与Python相同的结果.E. g.((-1 % 10) + 10) % 10.注意,它如何对正整数起作用:((17 % 10) + 10) % 10 == 17 % 10以及C实现的两种变体(正或负余数).

  • 什么a = n = - 11?我认为你的意思是((n%M)+ M)%M (3认同)
  • 我必须不同意第1点,并说在数学中两者都是正确的,因为它定义了同余类. (3认同)

for*_*ran 30

Python有一个"真正的"模运算,而C有一个余数运算.

它与负整数除法的处理方式有直接关系,即向0或负无穷舍入.Python向负无限而C(99)向0舍入,但在两种语言中(n/m)*m + n%m == n,%运算符必须以正确的方向进行补偿.

Ada更明确,并且同时具有modrem.


AnT*_*AnT 15

在C89/90中,具有负操作数的除法运算符和余数运算符的行为是实现定义的,这意味着根据实现,您可以获得任一行为.它只是要求运营商相互同意:从a / b = qa % b = r遵循a = b * q + r.如果代理严重依赖于结果,请在代码中使用静态断言来检查行为.

在C99中,您观察到的行为已成为标准.

事实上,这两种行为都有一定的逻辑.Python的行为实现了真正的模运算.您观察到的行为是C与向0舍入一致(它也是Fortran行为).

在C中优选向0舍入的原因之一是期望结果-a / b与之相同是很自然的-(a / b).在真正的模数行为的情况下,-1 % 10将评估为9,意味着-1 / 10必须为-1.这可能被视为相当不自然,因为它-(1 / 10)是0.

  • 整数除法是周期性的;(a+kb)/b = (a/b)+k。实数除法是周期性的、对称的。尝试定义整数除法以增加对称性将使其不再是周期性的,除非除数是奇数并且它是使用舍入到最接近的语义计算的。我认为周期性比对称性更重要,尽管其他人可能有所不同。 (2认同)

Bri*_*ndy 5

两个答案都是正确的,因为-1 modulo 109 modulo 10.

r = (a mod m)
a = n*q + r
Run Code Online (Sandbox Code Playgroud)

您可以确定|r| < |n|,但不确定 的价值r是什么。有2个答案,否定的和肯定的。


在 C89 中,虽然答案总是正确的,但模运算的确切值(他们将其称为余数)是不确定的,这意味着它可以是负结果或正结果。在 C99 中定义了结果。

如果你想要肯定的答案,如果你发现你的答案是否定的,你可以简单地加 10。

要使模运算符在所有语言上都相同,请记住:

n mod M == (n + M) mod M
Run Code Online (Sandbox Code Playgroud)

一般来说:

n mod M == (n + X * M) mod M
Run Code Online (Sandbox Code Playgroud)

  • C 中负数的模**是**定义的:通过以下语句: *如果商`a/b`是可表示的,则表达式`(a/b)*b + a%b`应等于`a` .* (6认同)
  • (我应该补充一点,在 C 中`%` 实际上并未定义为“模”运算符——它被定义为“余数”)。 (4认同)

aka*_*ice 5

执行欧几里得除法a = b*q + r,就像将分数四舍五入a/b为整数商q,然后计算余数r

您看到的不同结果取决于用于舍入商的约定......

如果向零舍入(截断),您将获得围绕零的对称性,如 C 中所示:

truncate(7/3) = 2
7 = 3*2 + 1

truncate(-7/3) = -2
-7 = 3* -2 - 1

truncate(7/-3) = -2
7 = -3* -2 + 1
Run Code Online (Sandbox Code Playgroud)

如果向负无穷大(下限)舍入,您将得到像 Python 中那样的余数:

floor(7/3) = 2
7 = 3*2 + 1

floor(-7/3) = -3
-7 = 3* -3 + 2

floor(7/-3) = -3
7 = -3* -3 - 2
Run Code Online (Sandbox Code Playgroud)

如果您四舍五入到最接近的整数(与您想要的任何值、偶数或远离零相关联),您将得到一个居中的模数:

round(7/3) = 2
7 = 3*2 + 1

round(8/3) = 3
8 = 3*3 - 1

round(-7/3) = -2
-7 = 3* -2 - 1

round(7/-3) = -2
7 = -3* -2 + 1
Run Code Online (Sandbox Code Playgroud)

您可以尝试实现自己的模数,并四舍五入到正无穷大(ceil),并且您会发明一个相当非常规的模数,但它仍然是一种模数......