取余和取模到底是不是一回事?对比Python、Java、C和C++中的%运算符

取余和取模到底是不是一回事?对比Python、JAVA、C和C++中的%运算符

  • 数学中的「取余」和「取模」
  • 计算机领域中的「取余」和「取模」
  • Python、Java、C和C++中的 `%` 运算符
    • Python:取模运算
    • Java:取余运算
    • C和C++:取余运算
    • 为什么一般用正除数


数学中的「取余」和「取模」

在纯数学中,当我们谈论整数除法 a ÷ b a \div b a÷b a a a 是被除数, b b b 是除数,且 b ≠ 0 b \not = 0 b=0) ,结果包含一个商(quotient) q q q 和一个余数(remainder) r r r,它们满足以下的关系:
a = b × q + r a = b \times q + r a=b×q+r
并且 r r r 满足 0 ≤ ∣ r ∣ < ∣ b ∣ 0 \le\lvert r\rvert\lt\lvert b\rvert 0r<b . 也就是说,余数的绝对值小于除数的绝对值,并且通常要求余数是非负的,即 0 ≤ r < ∣ b ∣ 0\le r\lt\lvert b\rvert 0r<b .

在这个定义下,「取余」和「取模」是等同的,目的都是求「满足上述等式的非负余数 r r r.

计算机领域中的「取余」和「取模」

在计算机领域中,取余运算(Remainder Operation)取模运算(Modulo Operation)并不完全相同。

由于历史原因和不同语言的设计选择,取余和取模在处理负数时会出现差异,通常,取余的结果符号同被除数,取模的结果总是非负的。而当 a a a b b b 都是正数时,所有编程语言和计算器对 a % ba mod b 的结果都是一致的,结果等于上述数学定义中的非负余数。

C、C++、Java、JavaScript、Go等采取取余运算;
Python、Ruby等采取取模运算;
而在某些语言中,取余和取模都有,比如Matlab、Julia。

下面对比一下 % 运算符在Python、Java、C和C++中的区别。

Python、Java、C和C++中的 % 运算符

Python:取模运算

先上代码示例,右边为输出结果:

print(10 % 3)   # 1
print(-10 % 3)  # 2
print(10 % -3)  # -2
print(-10 % -3) # -1

Python的运算方式为:a % b = a - b * floor(a / b),其中 floor() 向下取整。
因此上面的代码示例是这样计算的:

10 % 3 = 10 - 3 * floor(10 / 3)  =  10 - 3 * 3 = 1
-10 % 3 = -10 - 3 * floor(-10 / 3)  = -10 - 3 * (-4) = 2
10 % -3 = 10 - (-3) * floor(10 / -3)  = 10 - (-3) * (-4) = -2
-10 % -3 = -10 - (-3) * floor(-10 / -3)  = -10 - (-3) * 3 = -1

结果符号与除数相同。

前面我们说到,通常,取余的结果符号同被除数,取模的结果总是非负的,但这里也有负值,这是为什么?原因是一般情况下,我们总是使用正除数,这一点会在文章最后加以简单说明。

Java:取余运算

代码示例:

System.out.println(10 % 3);   // 1
System.out.println(-10 % 3);  // -1
System.out.println(10 % -3);  // 1
System.out.println(-10 % -3); // -1

Java的运算方式为:a % b = a - b * trunc(a / b),其中 trunc() 向零取整。
因此上面的代码示例是这样计算的:

10 % 3 = 10 - 3 * trunc(10 / 3)  =  10 - 3 * 3 = 1
-10 % 3 = -10 - 3 * trunc(-10 / 3)  = -10 - 3 * (-3) = -1
10 % -3 = 10 - (-3) * trunc(10 / -3)  = 10 - (-3) * (-3) = 1
-10 % -3 = -10 - (-3) * trunc(-10 / -3)  = -10 - (-3) * 3 = -1

结果符号和被除数相同。

C和C++:取余运算

代码示例:

printf("%d\n", 10 % 3);  // 1
printf("%d\n", -10 % 3);  // -1
printf("%d\n", 10 % -3);  // 1
printf("%d\n", -10 % -3);  // -1

运算方式和Java一样,不再赘述。

早期C语言中,负数的 % 运算并未统一,未对 a/b 取整方向做强制规定,由编译器或底层硬件平台自行决定。C99/C++11及之后,统一规定 a/b 向零取整。

为什么一般用正除数

模运算的经典使用场景中,除数一般都是正数,比如数论的同余关系、时间计算、循环访问数组、密码学的一些处理等,除数的物理意义往往是周期、长度等,这些都是正数。


个人笔记,能力有限可能有错漏,欢迎大家交流指正!能帮到你我会非常开心~

你可能感兴趣的:(java,python,c语言,c++)