引言:为什么关于模的问题如此受欢迎?
\n\n如果你曾经用Rust编写过代码并遇到过负数,你肯定会问过这样一个问题:“模函数在哪里?”。在Stack Overflow上,这个问题获得了数千次浏览,这并非没有原因。关键在于,Rust中的%运算符并不是模运算(modulus),而是取余运算(remainder)。区别很微妙,但至关重要,尤其是在算法、密码学或处理循环结构时。
在本文中,我们将探讨数学模与取余的区别,Rust如何处理负数,以及如何编写自己的计算真实模的函数。本文面向初学者,但对希望巩固知识的经验丰富的开发者也会有所帮助。
\n\n内容概要
\n- \n
- 什么是模和余:数学上的区别 \n
- 为什么Rust中的
%不是模运算 \n - 如何在Rust中编写模函数 \n
- 现实生活中的例子:循环索引和哈希 \n
- 结论和实用建议 \n
数学:模 vs 余
\n\n让我们从基础开始。在数学中,模(modulus)是一种返回小于除数的非负数的运算。例如:
\n- \n
7 mod 3 = 1(7除以3余1) \n -7 mod 3 = 2(因为-7 = 3 * (-3) + 2) \n
取余(remainder)是整数除法后剩下的部分。在大多数编程语言(C、C++、Java、Rust)中,%运算符返回的就是余数,如果被除数为负,余数也可能为负。例如:
- \n
7 % 3 = 1(一致) \n -7 % 3 = -1(在Rust中,因为-7 / 3 = -2,余数为-1) \n
为什么这很重要?想象一下,你正在编写一个时钟程序:时间范围0–23。如果现在是22点,你加上5小时,会得到凌晨3点。这可以通过模运算实现:(22 + 5) mod 24 = 3。但如果使用取余,当值为负数时就会出错。例如,在Rust中(-3) % 24会得到-3,而不是21。
为什么Rust中的%运算符是取余
\n\n在Rust中,%运算符被定义为取余运算。这在文档和标准库的行为中都有明确规定。原因在于历史沿袭:C语言和许多其他语言都是这样做的。Rust遵循“显式优于隐式”的原则,但在这里新手常常会掉入陷阱。
fn main() {\n println!("7 % 3 = {}", 7 % 3); // 1\n println!("-7 % 3 = {}", -7 % 3); // -1\n println!("7 % -3 = {}", 7 % -3); // 1(是的,除数的符号不影响结果)\n println!("-7 % -3 = {}", -7 % -3); // -1\n}\n\n请注意:结果的符号始终与被除数的符号一致。这是不是模运算。对于真正的模运算,结果必须始终为非负数。
\n\n如何在Rust中编写真正的模函数?
\n\nRust标准库没有提供内置的模函数(与Python不同,Python中的%就是模运算)。但编写一个非常简单。以下是最常见的方法——通过取余并修正。
整数的模函数
\n\nfn modulus(a: i32, b: i32) -> i32 {\n let r = a % b;\n if r < 0 { r + b.abs() } else { r }\n}\n\nfn main() {\n println!("modulus(7, 3) = {}", modulus(7, 3)); // 1\n println!("modulus(-7, 3) = {}", modulus(-7, 3)); // 2\n println!("modulus(7, -3) = {}", modulus(7, -3)); // 1(模始终为非负数)\n println!("modulus(-7, -3) = {}", modulus(-7, -3)); // 2\n}\n\n工作原理:我们取余数a % b。如果结果为负,则加上除数的绝对值