关于负数求模运算到底怎么求
最近有同事问到,负数到底怎么求模,结果的符号是和除数、被除数哪一个相关。因为这个问题好像以前也纠结过,但一直没有整理记录,因此特地搜索了相关资料,同时求证了在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 + 3
或
8 = 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语言中,运算原则就遵循尽量使商大