【问题标题】:What are the rules for modular arithmetic in C?C中的模运算规则是什么?
【发布时间】:2013-07-01 22:33:06
【问题描述】:

在早期的课程中,我被教导 n % d = r 并将其视为 n = d*q + r,其中 d 是除数,q 是商,r 是余数(注意余数永远不能是负数)。

例如,-111 mod 1110,因为 -111 = -11*-11 + 10(与 -111 = -11*10 -1 相反,看看这会给我们带来负余数)。

但是,当打印-111 % 11 的结果时,-1 是结果而不是10。为什么?这在技术上不是错误的吗?

【问题讨论】:

  • 啊,旧的负操作数模数 :-) 我记得 Python 在所有特殊情况下都正确(在数学意义上)。
  • 如果你想强制 C 的 '%' 操作符变成一个你期望的模数,只需这样做:((a % b) + b) % b
  • 在模运算的维基百科页面上有一个很好的表格,显示了每种语言如何有自己的模实现:en.wikipedia.org/wiki/Modulo_operation
  • 余浩的答案在我看来应该标记为答案,但值得注意的是,其他语言可能会有所不同。 Ada 有两个独立的函数:余数函数和模函数。当使用模运算符时,Python 处理负数的方式也不同。

标签: c modulus


【解决方案1】:

简答:

(a/b)*b + a%b 等于 a 的标准保证。

在 C99 中,/ 的除法结果将被截断为 0。 % 运算符的结果是肯定的,在这种情况下,-1

在 C89 中,除法的结果 / 可以被截断为负操作数。所以% 操作符的结果也是机器相关的。

长答案:

从 C99 6.5.5

5 / 运算符的结果是第一个操作数除以 第二; % 运算符的结果是余数。在这两种操作中,如果 第二个操作数为零,行为未定义。

6 整数相除时, / 运算符的结果是任何一个的代数商 小数部分被丢弃。如果商 a/b 是可表示的,则表达式 (a/b)*b + a%b 应等于 a;否则,a/b 和 a%b 的行为都是 未定义。

同一页上的脚注解释了/ 的工作原理,它说:

这通常被称为“向零截断”。

根据这条规则,-111 / 11只能是-10,不能是1。由于(a/b)*b + a%b必须等于a,所以-111 % 11就是-1

但是,K&R 第 2.5 章给出了不同的答案:

对于负操作数,/ 的截断方向和 % 结果的符号取决于机器,上溢或下溢时采取的操作也是如此。

据此,-110 都可以是合法结果。

原因在 C89 3.3.5:

当整数被除且除法不精确时,如果两个操作数都是正数,则 / 运算符的结果是小于代数商的最大整数,而 % 运算符的结果是正数。如果任一操作数为负,则 / 运算符的结果是小于代数商的最大整数还是大于代数商的最小整数是实现定义的,% 运算符的结果的符号也是如此。如果商 a/b 是可表示的,则表达式 (a/b)*b + a%b 应等于 a。

原来是从 C89 到 C99 的变化。

C99 Rationale 6.5.5 提供了一些历史原因:

在 C89 中,涉及负操作数的整数除法可以以实现定义的方式向上或向下舍入;目的是避免在运行时代码中产生开销以检查特殊情况并强制执行特定行为。然而,在 Fortran 中,结果总是会向零截断,而且开销似乎对数值编程社区来说是可以接受的。因此,C99 现在需要类似的行为,这应该有助于将代码从 Fortran 移植到 C。本文档第 7.20.6.2 节中的表格说明了所需的语义。

这是第 7.20.6.2 节中的表格:

numer denom quot rem
 7      3    2    1
–7      3   –2   –1
 7     –3   –2    1
–7     –3    2   –1

【讨论】:

  • 我认为只有 -1 是允许的——脚注 90(在 C99 标准的版本 n1256 中)提到文本“[...] 丢弃任何小数部分的代数商”,即“这通常被称为向 0 截断”。所以我认为这意味着-111 / 11 只能是-10,因此-111 % 11 只能是-1,因为(a/b)*b + a%b 必须等于a
  • 此外,按位运算符的功能不同!尽管 &>> 通常与 2 的幂的模和除法进行比较,但 >> 不会“向零截断”,因此 -5 >> 1 == -3-11 & 3 == 1(而 -11 % 4 == -3
  • 是的,这在 C99 中已更改。 C89 编译器可以做任何一个,并且通常做任何对硬件最方便的事情。
  • @iCode4Food int 的右移是实现定义的。
【解决方案2】:

对于模数,-1 将是错误的答案。

C 的 % 运算符虽然是余数运算符而不是模运算符 - 对于余数,10 或 -1 都是允许的。

【讨论】:

  • 请定义模数的含义。似乎有不同的定义,具体取决于上下文,对于其中一些定义,ISTM 认为 -1 是正确的。
  • @RudyVelthuis:“模数”通常与 modular arithmetic 相关联。至少作为模算术通常定义,只允许非负数。
  • 正如我所说,这取决于上下文。如果您暗示欧几里得除法,那么确实,余数总是正数。但在其他情况下,情况不一定如此。特别是因为这是一个编程论坛,应该知道“模数”的含义可能因语言而异。
  • @RudyVelthuis:我不同意。模数意味着什么。大多数语言实现的东西在某些情况下可能相似,但(在大多数情况下)在其他情况下会有所不同。
  • 那你能给出一个模数的权威定义吗? ISTM 认为模数在不同的上下文中意味着不同的东西。
【解决方案3】:

% 运算符的实现使得a == b * (a / b) + (a % b) 为真,我们使用整数除法。

在这种情况下-111 / 11-10,所以-111 == 11 * -10 + x 满足x == -1

【讨论】:

  • IOW,a % b = a - (a / b) * b。由于-111 / 11产生-10,所以结果为-111 - (-10 * 11),即-1
猜你喜欢
  • 2017-06-28
  • 2010-12-11
  • 2022-04-19
相关资源
最近更新 更多