引子

%C/C++Python

为什么同一种运算会出现区别呢?

因为在被除数和除数的符号不同时,余数的符号会产生歧义,可正可负。

取模定理和求余定理

前提:设a=kb+ra = kb + ra=kb+r, 且满足0ak0 \leq \left | a\right | \leq \left | k\right |0≤∣a∣≤∣k∣

给定aaa 和kkk,求mod(a,k)mod(a, k)mod(a,k) 和rem(a,k)rem(a, k)rem(a,k)

显然,对于满足上述两个前提条件,如果aaa 无法整除kkk,则存在两对(b,r)(b, r)(b,r),其中一对的rrr 为正数(正余数),另一对rrr 为负数 (负余数) ,我们也可以用一个时钟从0点顺时针和逆时针走到某一点的两种方案来理解。

结果:正因为上述歧义,不同语言的取模的定义可能不一样,最常见的是:

取模:kkk 更趋于负无穷大时的rrr,即mod(a,b)mod(a, b)mod(a,b)

求余:kkk 更趋于 0 是的rrr, 即rem(a,b)rem(a, b)rem(a,b)

举例

例1

mod(7,3)=1mod(7, 3) = 1mod(7,3)=1 和rem(7,3)=1rem(7, 3) = 1rem(7,3)=1

此时的两对(b,r)(b, r)(b,r) 分别为:

  • (2,1)(2, 1)(2,1) , 即7=23+17 = 2 * 3 + 17=2∗3+1
  • (3,2)(3, -2)(3,−2) ,即7=33+(2)7= 3 * 3 + (-2)7=3∗3+(−2)

因为b1=2,  b2=3b1 = 2, \ \ b2= 3b1=2,  b2=3

所以,b1b1b1 更趋近于负无穷大,所以取模操作时的rrr 取第1组,r=1r = 1r=1;

同理,因为b1b1b1 更趋近于负无穷大,所以取余操作的rrr 也取第一组,r=1r = 1r=1。


例2

mod(7,3)=2mod(7, -3) = -2mod(7,−3)=−2 和rem(7,3)=1rem(7, -3) = 1rem(7,−3)=1

此时的两对(b,r)(b, r)(b,r) 分别为:

  • (2,1)(-2, 1)(−2,1) , 即7=(2)(3)+17 = (-2) * (-3) + 17=(−2)∗(−3)+1
  • (3,2)(-3, -2)(−3,−2) ,即7=(3)(3)+(2)7= (-3) * (-3) + (-2)7=(−3)∗(−3)+(−2)

因为b1=2,  b2=3b1 = -2, \ \ b2= -3b1=−2,  b2=−3

所以,b2b2b2 更趋近于负无穷大,所以取模操作时的rrr 取第2组,r=2r = -2r=−2;

而因为b1b1b1 更趋近于 0 ,所以取余操作时的rrr 取第二组,r=1r = 1r=1


例3

mod(7,3)=2mod(-7, 3) = 2mod(−7,3)=2 和rem(7,3)=1rem(-7, 3) = -1rem(−7,3)=−1

此时的两对(b,r)(b, r)(b,r) 分别为:

  • (2,1)(-2, -1)(−2,−1) , 即7=(2)3+(1)7 = (-2) * 3 + (-1)7=(−2)∗3+(−1)
  • (3,2)(-3, 2)(−3,2) ,即7=(3)3+27= (-3) * 3 + 27=(−3)∗3+2

因为b1=2,  b2=3b1 = -2, \ \ b2= -3b1=−2,  b2=−3 , 所以b2b2b2 更趋近于负无穷大,所以取模操作时的rrr 取第2组,r=2r = 2r=2;

而因为b1b1b1 更趋近于 0 ,所以取余操作时的rrr 取第二组,r=1r = -1r=−1


例4

mod(7,3)=1mod(-7, -3) = -1mod(−7,−3)=−1 和rem(7,3)=1rem(-7, -3) = -1rem(−7,−3)=−1

此时的两对(b,r)(b, r)(b,r) 分别为:

  • (2,1)(2, -1)(2,−1) , 即7=(2)(3)+(1)7 = (2) * (-3) + (-1)7=(2)∗(−3)+(−1)
  • (3,2)(3, 2)(3,2) ,即7=3(3)+27= 3 * (-3) + 27=3∗(−3)+2

因为b1=2,  b2=3b1 = 2, \ \ b2= 3b1=2,  b2=3, 所以,b1b1b1 更趋近于负无穷大,所以取模操作时的rrr 取第1组,r=1r = -1r=−1;

而因为b1b1b1 更趋近于 0 ,所以取余操作时的rrr 也取第一组,r=1r = -1r=−1


编程语言中的 % 符号

C/C++%
1
2
3
4
5
6
7
8
9
10
11
12
13
int main()
{
std::cout << (7 % 3) << std::endl;
std::cout << (7 % -3) << std::endl;
std::cout << (-7 % 3) << std::endl;
std::cout << (-7 % -3) << std::endl;
}

// output:
1
1
-1
-1
Java%
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
public static void main(String []args) {
System.out.println(7 % 3);
System.out.println(7 % -3);
System.out.println(-7 % 3);
System.out.println(-7 % -3);
}
}
// output:
1
1
-1
-1
Rust%
1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
println!("{}", 7 % 3);
println!("{}", 7 % -3);
println!("{}", -7 % 3);
println!("{}", -7 % -3);

}
// output:
1
1
-1
-1
Python%
1
2
3
4
5
6
7
8
9
print(7 % 3)
print(7 % -3)
print(-7 % 3)
print(-7 % -3)
// output:
1
-2
2
-1
Golang%
1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
fmt.Println(7 % 3)
fmt.Println(7 % -3)
fmt.Println(-7 % 3)
fmt.Println(-7 % -3)

}
%
m % n = m - n * (m / n)
5 / -3 = -15 % (-3) = 5 - (-3) *(5 / -3) = 2