引子
%C/C++Python为什么同一种运算会出现区别呢?
因为在被除数和除数的符号不同时,余数的符号会产生歧义,可正可负。
取模定理和求余定理
前提:设a=kb+r, 且满足0≤∣a∣≤∣k∣
给定a 和k,求mod(a,k) 和rem(a,k)
显然,对于满足上述两个前提条件,如果a 无法整除k,则存在两对(b,r),其中一对的r 为正数(正余数),另一对r 为负数 (负余数) ,我们也可以用一个时钟从0点顺时针和逆时针走到某一点的两种方案来理解。
结果:正因为上述歧义,不同语言的取模的定义可能不一样,最常见的是:
取模:k 更趋于负无穷大时的r,即mod(a,b)
求余:k 更趋于 0 是的r, 即rem(a,b)
举例
例1
mod(7,3)=1 和rem(7,3)=1
此时的两对(b,r) 分别为:
- (2,1) , 即7=2∗3+1
- (3,−2) ,即7=3∗3+(−2)
因为b1=2, b2=3
所以,b1 更趋近于负无穷大,所以取模操作时的r 取第1组,r=1;
同理,因为b1 更趋近于负无穷大,所以取余操作的r 也取第一组,r=1。
例2
mod(7,−3)=−2 和rem(7,−3)=1
此时的两对(b,r) 分别为:
- (−2,1) , 即7=(−2)∗(−3)+1
- (−3,−2) ,即7=(−3)∗(−3)+(−2)
因为b1=−2, b2=−3
所以,b2 更趋近于负无穷大,所以取模操作时的r 取第2组,r=−2;
而因为b1 更趋近于 0 ,所以取余操作时的r 取第二组,r=1
例3
mod(−7,3)=2 和rem(−7,3)=−1
此时的两对(b,r) 分别为:
- (−2,−1) , 即7=(−2)∗3+(−1)
- (−3,2) ,即7=(−3)∗3+2
因为b1=−2, b2=−3 , 所以b2 更趋近于负无穷大,所以取模操作时的r 取第2组,r=2;
而因为b1 更趋近于 0 ,所以取余操作时的r 取第二组,r=−1
例4
mod(−7,−3)=−1 和rem(−7,−3)=−1
此时的两对(b,r) 分别为:
- (2,−1) , 即7=(2)∗(−3)+(−1)
- (3,2) ,即7=3∗(−3)+2
因为b1=2, b2=3, 所以,b1 更趋近于负无穷大,所以取模操作时的r 取第1组,r=−1;
而因为b1 更趋近于 0 ,所以取余操作时的r 也取第一组,r=−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