繁体   English   中英

C 和 Python - 模 (%) 运算的不同行为

[英]C and Python - different behaviour of the modulo (%) operation

我发现相同的 mod 操作会根据使用的语言产生不同的结果。

在 Python 中:

-1 % 10

生产9

在 C 它产生-1

  1. 哪个是正确的模数?
  2. 如何使 C 中的 mod 操作与 Python 中的操作相同?
  1. 两种变体都是正确的,但是在数学(特别是数论)中,Python 的是最常用的。
  2. 在 C 中,您执行((n % M) + M) % M以获得与 Python 中相同的结果。 例如 ((-1 % 10) + 10) % 10 请注意,它仍然适用于正整数: ((17 % 10) + 10) % 10 == 17 % 10 ,以及 C 实现的两种变体(正余数或负余数)。

Python 有一个“真”模运算,而 C 有一个余数运算。

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

Ada 更明确,并且两者都有,如modrem

在 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。

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

r = (a mod m)
a = n*q + r

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


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

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

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

n mod M == (n + M) mod M

一般来说:

n mod M == (n + X * M) mod M

执行欧几里德除法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

如果你向负无穷大(下限)四舍五入,你会得到像 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

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

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

您可以尝试实现自己的模数,并朝正无穷大 (ceil) 取整,并且您会发明一个非常规的模数,但它仍然是一种模数......

从 python 3.7 开始,您还可以使用math内置模块中的.remainder()

Python 3.7.0a0 (heads/master:f34c685020, May  8 2017, 15:35:30)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import math
>>> math.remainder(-1, 10)
-1.0

文档

返回 x 相对于 y 的 IEEE 754 样式余数。 对于有限 x 和有限非零 y,这是差x - n*y ,其中 n 是最接近商x / y精确值的整数。 如果x / y正好位于两个连续整数的中间,则最近的偶数整数用于n 因此,余数r = remainder(x, y)总是满足abs(r) <= 0.5 * abs(y)

特殊情况遵循 IEEE 754:特别是,对于任何有限的 x, remainder(x, math.inf)是 x,而对于任何非 NaN x, remainder(x, 0)remainder(math.inf, x)引发 ValueError。 如果余数运算的结果为零,则该零的符号与 x 相同。

在使用 IEEE 754 二进制浮点数的平台上,此操作的结果始终可以精确表示:不引入舍入误差。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM