【问题标题】:C remainder/modulo operator definition for positive arguments正参数的 C 余数/模运算符定义
【发布时间】:2019-11-08 14:12:11
【问题描述】:

我在一个我应该修复的程序中发现了一个定义了 mod 函数的函数:

int mod(int a, int b)
{
  int i = a%b;
  if(i<0) i+=b;
  return i;
}

有人告诉我,ab 将永远是积极的...

嗯? if(i&lt;0)?

参数是that

模运算的结果是一个等价类,可以选择该类的任何成员作为代表

只是事后才考虑

...;然而,通常的代表是最小正余数,属于该类的最小非负整数,即欧几里得除法的余数。然而,其他约定也是可能的。

这意味着6 % 7 可以返回6(到目前为止还不错),但也可以返回-1。嗯……真的吗? (让我们忽略所呈现的实现不能处理所有情况的事实。)

我知道模运算是这样的,在数学上是正确的。但是后来有人告诉我,C % 实际上“没有实现模运算符,而是实现了余数”。

那么,C 是如何定义% 运算符的呢?

我只在 C-Draft 中找到

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

这是否意味着6 % 7 总是6?或者也可以是-1

【问题讨论】:

    标签: c division modulo integer-division


    【解决方案1】:

    按照标准:

    整数除法时,/ 运算符的结果是 丢弃任何小数部分的代数商。如果商 a/b 是可表示的,表达式(a/b)*b + a%b 应等于a。 [ISO/IEC 9899:2011: 6.5.5]

    这意味着a 的符号保留在模中。

     17 %  3  ->  2
     17 % -3  ->  2
    -17 %  3  -> -2
    -17 % -3  -> -2
    

    所以不,6%7 不能是 -1,因为提醒必须具有相同的股息符号。

    【讨论】:

    • 我没有问过标志
    • 但它回答了你的问题。它不能是-1 因为6 是肯定的。
    【解决方案2】:

    永远:

    • a == (a/b)*b + a%b
    • abs(a%b) &lt; abs(b)
    • 如果ab 为正,则a % b 为正。

    自C99以来,

    • a/b == trunc(a/b)
    • a%b 要么是 0,要么是 a 的符号。

    认为6 % 7 可能是-1 可能是由于错过了ab 的结果始终得到保证的事实,并且错过了C99 中的变化。

    【讨论】:

      【解决方案3】:

      这是否意味着 6 % 7 总是 6?或者也可以是-1?

      根据this documention

      剩余

      二元运算符 % 产生除法的余数 第二个操作数的第一个操作数(在通常的算术之后 转换)。

      [...]

      当通常算术转换后的类型是整数类型时, 结果是代数商(不是分数),四舍五入 实现定义的方向(C99 前)向零截断 (C99 起)

      所以6 / 7 将是06 % 7 将是6 - 0,即6

      关于模运算和等价类的说法很有趣,但这不是它在 C(和大多数其他编程语言)中的工作方式。

      此外,即使是这样,-8 不会属于同一个等价类吗?那么if(i&lt;0) i+=b; 就解决不了问题了。

      但是后来有人告诉我,C % 实际上“没有实现 模运算符,但余数”。

      好点子。在我链接的文档中,它被称为“Remainder”。

      【讨论】:

      • 确实,您需要类似于:while(i&lt;0) i+=b
      • 谢谢,我有同样的解释。而我的评论“让我们忽略......”正是关于你的最后一句话。
      • 您链接到的文档是错误的。 C89 标准第 6.3.5 节规定“当整数被除且除法不精确时,如果两个操作数都是正数,则 / 运算符的结果是小于代数商的最大整数,并且 % 运算符的结果是正数。”
      • / 的舍入和 % 的符号仅在一个或两个操作数为负时才被隐式定义(C99 之前):“...如果任一操作数为负,则结果是否/ 运算符的最大整数小于或等于代数商或大于或等于代数商的最小整数是实现定义的,% 运算符的结果的符号也是如此。如果商 a/ b 是可表示的,表达式 (a/b) *b + a%b 应该等于 a。"
      • @PaulHankin 谢谢!这比文档更可信。您是否有此链接,以便我可以将其添加到答案中?
      【解决方案4】:

      当一个数字或另一个可能为负时,至少有三种不同的方法来定义除法和余数算法。 (请参阅this Wikipedia article——尤其是nice picture there——了解更多详情。)

      但是,如果您知道将正数除以正数,则没有任何歧义。除法和余数的所有三个定义都表明,如果将正数除以正数,则会得到正商和正余数。

      在这三个选项中,C 使用了一种称为“截断除法”的选项。但是,同样,对于正数,它没有任何区别。 (曾几何时,由编译器决定是使用截断还是“欧几里得”除法,但事情只解决了一个定义,即 C 标准的几个修订版。)

      这是否意味着6 % 7 总是6?或者也可以是-1

      是的,6 % 7 始终为 6(在 C 中,并且在三个定义中的任何一个下)。

      另见this epic, related question

      【讨论】:

        猜你喜欢
        • 2014-07-17
        • 1970-01-01
        • 2013-05-17
        • 2017-04-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多