运算符(%):关于负数求模该如何求解

2019-04-14 08:25发布

关于负数求模运算到底怎么求

最近有同事问到,负数到底怎么求模,结果的符号是和除数、被除数哪一个相关。因为这个问题好像以前也纠结过,但一直没有整理记录,因此特地搜索了相关资料,同时求证了在Java中求模运算的操作 对于正整数的求模运算,我们都很熟悉。但是负数如何求模,我们来探究一下,以下主要验证是在Java和C语言开发中的负数求模操作,其它如C++、Python等语言,由于环境限制,并未验证。 自然数的求模运算定义
如果a和d是两个自然数,d非零,可以证明存在两个唯一的整数 q 和 r,满足 a = qd + r 且0 ≤ r < d。其中,q 被称为商,r 被称为余数
那么对于负数这样的定义是否也适用呢?我们来假设一个例子,被除数为负数,除数为正数,求解(-8) % 5的结果。我们发现,假如按照自然数求模运算的定义,可以有这样的一个表达式:-8 = -2 * 5 + 2,根据这个表达式,2应该为(-8) % 5的结果,但实际是否这样呢。经Java测试验证,(-8) % 5结果为 -3,经C测试验证,(-8) % 5结果也为 -3。我们再到百度计算器中来计算一下该运算结果:。 语言 求解 商 余数 Java (-8) % 5 -1 -3 C (-8) % 5 -1 -3 百度计算器 (-8) % 5   2 咦!和我们在Java测试中的结果不一样。那么问题就出来了... 看来我们是不能直接把自然数的求模法则用在负数的求模上。实际上,在整数范围内,自然数的求余法则并不被很多人所接受,大家大多认可的是下面的这个定义2
如果a 与d 是整数,d 非零,那么余数 r 满足这样的关系:a = qd + r , q 为整数,且0 ≤ |r| < |d|
那么这样一来,有负数的求模操作就不是我们想象中那么简单了。如(-8) % 5可以表示为:-8 = -2 * 5 + 2,也可以表示为:-8 = -1 * 5 + (-3),即 2 和 -3 都是(-8) % 5的正确结果。这种情况下,对于取模运算,可能有两个数都符合要求。我们把 2 和 -3 分别叫做正余数和负余数。通常,当除以 d 时,如果正余数为r1,负余数为r2,那么有:
r1 = r2 + d
我们再来看一种情况。求解8 % (-5)的结果,现在我们将除数换成负数,被除数换成正数。根据定义2,我们可以将8 % (-5)表示为:8 = (-2) * (-5) + (-2),也可以表示为:8 = (-1) * (-5) + 3,即 -2 和 3 都是 8 % (-5) 的结果。在Java测试验证时,结果为 3,C语言测试验证时,结果也为 3。 语言 求解 商 余数 Java 8 % (-5) -1 3 C 8 % (-5) -1 3 百度计算器 8 % (-5)   -2 可以推断,被除数与除数中其中一个数字为负数,在进行求模运算时,Java和C语言中通常会尽量让商大一些,如在8 % (-5)中,以-1为商而不是以-2为商;在(-8) % 5中,以-1为商而不是以-2为商。 那么如果被除数和除数均为负数时,求模运算是如何进行的呢? 我们来分析一下(-8) % (-5),可以表示为:-8 = 1 * (-5) + (-3)-8 = 2 * (-5) + 2,那么按照前述推断,是否2为(-8) % (-5)的结果呢,我们到Java和C中测试一下,结果如下: 语言 求解 商 余数 Java (-8) % (-5) 1 -3 C (-8) % (-5) 1 -3 百度计算器 (-8) % (-5)   -3 结果却出乎我们的意料,但Java、C、百度计算器中返回的结果却是一致的。 如果我们再按照定义2分析一下,被除数与除数均为正数时的情况,8 % 5可表示为:8 = 1 * 5 + 38 = 2 * 5 + (-2),我们知道,在Java、C中,8 % 5的结果为3,即商在取值是选择了较小值,与(-8) % (-5)结果为-3所对应取的商值是同一规律:尽量让商小一些 语言 求解 商 余数 Java 8 % 5 1 3 C 8 % 5 1 3 百度计算器 8 % 5   3

总结

根据以上分析,我们有理由做出如下总结
  • 对于同号整数求模,运算原则应遵循尽量使商小
  • 对于异号整数求模,在Java、C语言中,运算原则就遵循尽量使商大