【问题标题】:C: The Math Behind Negatives and RemainderC:负数和余数背后的数学
【发布时间】:2013-09-14 04:51:47
【问题描述】:

这似乎是在处理 Remainder/Mod 时被问到的第一件事,我有点碰壁了。我正在教自己用一本教科书和一小段 C 代码来编程。

看到我真的没有教练说,“不,不。它实际上是这样工作的”,我想我会在这里尝试一下。不过,我还没有找到数学部分的结论性答案。

所以...我的印象是这种情况非常罕见,但我仍然想知道在闪亮的编译下发生了什么。另外,这本教科书希望我根据 C89 标准提供使用负余数时可能出现的所有值。是否有人可以检查一下这个数学是否正确?

1) 9%4
9 - (2) * 4 = 1     //this is a value based on x - (x/y) * y
(2) * 4 + (1) = 9     //this is a check based on (x/y) * y + (x%y) = x

2) -9%4
9 - (2) * 4 = 1; 9 - (3) * 4 = -3   //these are the possible values
(2) * 4 + (1) = 9; (3) * 4 + (-3) = 9    //these are the checks

3) 9%-4
Same values as #2?? 

我尝试在表达式中使用负数进行计算,并得出了 17 和 -33 等荒谬的东西。 #3 也是 1 和 -3 吗??

4) -9%-4
Same as #1??

在代数除法中,负号“取消”。他们在这里做同样的事情,还是发生了其他事情?

我认为最让我困惑的是负面因素。我在学校(5-6 年前)学习代数的方式是“依附”于他们的数字。在编程中,由于它们是一元运算符,不是这样吗?示例:在 #2 上填写 x 的值时,x = 9 而不是 x = -9。

我真诚地感谢任何帮助。

【问题讨论】:

  • 这个问题有帮助吗:stackoverflow.com/questions/8318791/…
  • @Barmar 这是一个很好的链接,先生。讨论实际上是民间的,而且观点很有趣。
  • 目前尚不清楚如何从正负 9 和 4 的任意组合的余数运算中获得 -33 或 17。您应该显示 short, self-contained, compilable example
  • @Barnar,非常感谢。
  • @Eric Postpischil,这些数字是通过尝试用长手计算余数得到的,在上面的公式中使用 -9 和 -4。没有代码可以配合它们,只是为了查看发生了什么而进行数字运算。

标签: c math modulus


【解决方案1】:

这里你需要关于余数的数学定义。

给定两个整数m,d,我们说rm除法的余数 > 和 d 如果 r 满足两个条件:

  • 存在另一个整数 k 使得 m == k * d + r
  • 0 <= r < d.

对于正数,在 C 中,我们有 m % d == rm / d == k,只需遵循上面的定义。

根据定义,可以得到 3 % 2 == 1 和 3 / 2 == 1。
其他例子:

4 / 3 == 1 和 5 / 3 == 1,尽管 5.0/3.0 == 1.6666(其中 将四舍五入到 2.0)。

4 % 3 == 1 和 5 % 3 == 2。

你也可以相信公式r = m - k * d,它在C中写成:

m % d == m - (m / d) * d

但是,在标准 C 中,整数除法遵循以下规则:round to 0
因此,对于负操作数,C 提供了与数学结果不同的结果。
我们会:

(-4) / 3 == -1, (-4) % 3 == -1(在 C 中),但在简单的数学中:(-4) / 3 = -2, (-4) % 3 = 2。

在简单的数学中,余数总是非负的,并且小于abs(d)
在标准 C 中,余数总是有第一个操作数的符号。

+------------------------+ |米 | d | / | % | +-----+-----+-----+------+ | 4 | 3 | 1 | 1 | +-----+-----+-----+------+ | -4 | 3 | -1 | -1 | +-----+-----+-----+------+ | 4 | -3 | -1 | 1 | +-----+-----+-----+------+ | -4 | -3 | 1 | -1 | +------------------------+

备注:此描述(在否定情况下)仅适用于标准 C99/C11。你必须小心你的编译器版本,并做一些测试。

【讨论】:

  • 谢谢!这更接近我正在寻找的东西。我确实注意到您使用了 C99 的标准。我知道 C89 可以给出两个不同的答案,因为负除法问题的答案将向上或向下舍入,这将改变该公式给出的值——因此产生两个可能的值。所以,让我问一下......假设你正在计算这个速记,就像我一样,你有 -4%3。在 m - (m/d) * d 中,m = -4 吗?
  • 我不明白你的 lats 表达。在标准 C(自 1999 年以来)中,您始终可以相信以下两条规则:(1) 整数除法舍入为 0,相等 m % d == m - (m/d)*d 始终为真。这意味着(-4)/3 == -1(根据规则(1))并且根据规则(2):(-4)%3 == (-4) - ((-4)/3)*3 == (-4) - (-1)*3 == (-4) - (-3) == -4+3 == -1。这样,您还拥有:(-4) %% 3 == - (4%3)。或者好吧,你也可以说abs(-4%3) == abs(-4)%3
  • 如果你问的是运算符优先级,那么是的,-4%3 和 (-4) % 3 一样,所以 m%n 在这里表示 m == -4。
【解决方案2】:

就像 Barmar 的链接答案一样,模数在数学意义上意味着数字是环的同一类(我的代数理论有点生疏,所以很抱歉这些术语可能有点松散使用:))。

所以模数 5 意味着您有一个大小为 5 的环。即 0、1、2、3、4 当您将 1 加到 4 时,您又回到了零。所以 -9,-4,1,6,11,16 都是相同的模 5,因为它们都是等价的。这实际上对于各种代数定理非常重要,但对于普通程序员来说几乎没用。

基本上没有指定标准,因此为负数返回的模数必须是那些等效的数字类别之一。这不是剩余物。如果你想要基本的整数除法,在这种情况下你最好的选择是在做模运算符时对绝对值进行运算。如果您正在使用更高级的技术(如公钥加密),您可能需要多复习一下您的数学知识。

现在我想说在这种情况下仍然使用正整数,并享受有趣的编程有趣的东西。

【讨论】:

  • "对于普通程序员来说,它几乎没用" ???我一定是不正常的,因为没有它我就活不下去。我敢打赌,其他许多人也是如此。仅仅因为你从来没有理由使用某样东西,就不要说其他的。
  • 当我发现自己在使用负片时,我一定会尽我所能避免使用带有 mod/remainder 的底片。我很感激你的回答! :)
  • @woodchips: 在什么情况下 (-7 / 3 == -2, -7 % 3 == -1) 比 (-7 / 3 == -3, - 7 % 3 == 2)?我知道这就是 FORTRAN 和许多处理器的有符号除法指令的行为方式,但我很好奇这多久有用。我遇到过很多情况,纯粹是因为这样的规则,我不得不为负数添加特殊代码,而且我想不出任何情况下我必须在后一个定义下编写这样的代码,但前一个定义会有让我免于这样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-22
相关资源
最近更新 更多