取模运算到底在算什么:取余、负数取模与编程 % 运算符全解
从 17 mod 5 = 2 讲起,把取模(求余数)的定义、判奇偶循环哈希时钟等用途、负数取模的两种约定、和向下取整商的关系,以及各语言 % 运算符的差异一次说清。
取模运算到底在算什么:取余、负数取模与编程 % 运算符全解
取模运算就是求两个整数相除之后的余数。写成 a mod n,结果是 a 除以 n 留下的那一截。最直接的例子:17 mod 5 = 2,因为 17 里装得下 3 个完整的 5,装完还剩 2。这个剩下的 2 就是模运算的全部产出。
它看起来很小,却几乎钻进了每一行需要"循环"或"分类"的代码里。下面我把它的定义、常见用途、负数的坑、和取整的关系,以及编程里 % 运算符的脾气,逐个讲清楚。
一个公式:a = q·n + r
理解取模,只要记住一条除法恒等式:a = q·n + r。这里 q 是商,r 是余数,而 a mod n 取的就是 r。
拿 17 mod 5 验一遍:q = 3,n = 5,3·5 = 15,r = 17 − 15 = 2。三个数严丝合缝地拼回 17。这条恒等式是后面所有讨论的地基,负数取模为什么有歧义,也得回到它身上。
取模能干什么:判奇偶、循环、哈希、时钟
判奇偶 是最朴素的用途。任何整数 mod 2,结果不是 0 就是 1。等于 0 是偶数,等于 1 是奇数。所以 n % 2 == 0 就是"n 是不是偶数"的标准写法,比拆位、比转字符串都干净。
循环回绕 是它真正发光的地方。你有 7 种颜色想轮流用,计数器一直加,只要 颜色[i % 7],下标就永远落在 0 到 6 之间,加到天荒地老也不会越界。环形缓冲区、轮播图、分页器,底层都是这一招。
哈希分桶 同理。一个哈希值动辄上亿,你的桶只有 1009 个,hash mod 1009 一步就把它压进 0 到 1008 的合法范围。模数取质数(比如 1009)还能让分布更均匀,减少冲突。
时钟运算 是教科书例子。现在 14 点,过 5 小时是几点?(14 + 5) mod 12 = 7,模运算天然处理"绕圈"这件事。星期几、月份、角度,凡是周期性的量,都靠它回绕。
负数取模:两种都对的答案
这是最容易栽的地方。-7 mod 3 等于几?
答案取决于约定,两个都对。回到 a = q·n + r:如果让商向零截断(trunc),-7 ÷ 3 ≈ -2.33 截成 -2,那么 r = -7 − (-2)·3 = -1,余数带着被除数的负号。这是 JavaScript、C、Java、Go、Rust 里 % 给的结果。
但如果让商向下取整(floor),-2.33 取成 -3,那么 r = -7 − (-3)·3 = 2,余数恒为非负。这是数学家口中的欧几里得取模,也是 Python 的 % 给的结果。所以同一个 -7 % 3,C 里是 -1,Python 里是 2,从 C 移植代码到 Python 一遇到负数就出错,根子就在这。
实际写代码时,如果你要的是恒非负的那个(给数组下标、给哈希桶用),记住这个万能写法:((a % n) + n) % n。先取一次模,加回一个 n 把负数顶成正的,再取一次模收进区间。
取模和取整是一对
取模和取整商其实是同一次除法的两个产物,谁也离不开谁。前面那条 a = q·n + r 里,q 是商、r 是余数,两个一起才能把 a 还原。
关键在于:你用哪种取整,就决定了你拿到哪种余数。向零截断配出截断取余,向下取整配出欧几里得取模。这也是为什么负数那一节里,商的取整方式一变,余数的符号就跟着翻。想看清这层联动,可以用 取模计算器 把两种余数和对应的向下取整商并排算出来,一眼对上。
我自己踩过的坑
我第一次写轮播图时,用户往左划,下标从 0 减到 -1,我想当然地以为 -1 % 列表长度 会绕到末尾。结果 JavaScript 里 -1 % 5 给我 -1,数组直接读出 undefined,整个组件白屏。当时盯着控制台想了半天才反应过来:% 在这里取的是被除数的符号。后来全项目的回绕都换成了 ((i % n) + n) % n,再没翻过车。这也是我现在习惯把两种约定都验一遍再写下去的原因。
各语言 % 运算符速查
大多数 C 系语言(C、C++、Java、JavaScript、Go、Rust)的 % 是截断取余,余数符号跟被除数;Python 和 Ruby 的 % 是向下取整版,余数符号跟除数。所以判奇偶在哪种语言都安全(非负输入两者一致),但负数下标回绕一定要先想清楚你用的是哪一派。
取模再往上还连着模幂(aᵇ mod n)和模逆元,密码学里 RSA、Diffie-Hellman 全靠它们。如果你在算这类大数,可以顺手看看 质数计算器,互质和质因数是模逆元存在与否的前提。
记住一句话就够了:取模给的是余数,而余数的脾气由取整方式决定。把这层关系想透,循环、哈希、时钟、负数下标这些问题就都不再是问题。
Made by Toolora · Updated 2026-06-13