取模运算符%不用于计算总页数或判断末页,而专用于记录序号转页码(如Math.floor(index / pageSize) + 1)和循环索引回环(如((i % n) + n) % n),总页数须向上取整,末页判断唯一安全方式是currentPage >= totalPages。

取模运算符(%)在分页和循环定位中不是万能钥匙,但用对地方就能省掉一堆条件判断。关键不在“用不用%”,而在于它该出现在哪个环节、配合什么算术逻辑才不翻车。
分页总数必须向上取整,别信简单除法
总条数 103、每页 10 条,结果是 11 页,不是 10 页。用 total / pageSize 直接转整数或 Math.floor(total / pageSize) 都会漏掉最后一页。
- 推荐写法(无浮点误差,通用):Math.floor((total + pageSize - 1) / pageSize)
- 更直观的取模判断:total % pageSize === 0 ? total / pageSize : Math.floor(total / pageSize) + 1
- Java 中可用 (int) Math.ceil((double) totalCount / pageSize),但注意强转前必须转 double,否则整数除法先截断
当前页数据起始位置靠减法和乘法,不是取模
offset 是从第几条开始取,不是“余数”。页码 page 从 1 开始,所以第 1 页 offset = 0,第 2 页 offset = 10(假设每页 10 条)。
- 固定公式:offset = (page - 1) * pageSize
- 前端传 page=1,后端不能当成 offset=1,否则跳过首条
- 用 slice 做前端数组分页时,start = (page - 1) * pageSize,end = Math.min(start + pageSize, arr.length)
取模真正派上用场的地方:记录序号转页码、循环索引回环
% 不是用来算总页数,也不是用来判“是不是最后一页”——currentPage % totalPages === 0 是典型误用,这是循环检测,不是边界判断。
- 已知某条数据的全局索引 index(从 0 开始),它在第几页?Math.floor(index / pageSize) + 1 —— 这里 % 只是辅助剥离余数,非主逻辑
- 实现 tab 切换、轮播图、环形缓冲区时,下标可能为负或超长:((i % n) + n) % n 才能把任意整数 i 安全映射到 [0, n) 区间
- 比如数组长度为 5,i = -1 → 结果是 4;i = 7 → 结果是 2;这个表达式跨语言通用,比 if 分支还稳
分库分表或动态增删时,取模本身没错,错的是输入过期
当数据实时变化,total 统计值没刷新,再准的取模公式也会导出错误页码。游标分页(cursor-based)完全绕开 offset 和 %,此时取模无用武之地。
- MySQL 的 LIMIT offset, size 在大偏移量下性能差,根源是扫描行数随 offset 增大而线性上升,和 % 无关
- 哈希分片(如 order_id % 3)靠取模均匀分数据,但分页查询时无法保证全局排序,这时得用归并排序或改用范围分片+覆盖索引
- PHP 中用 ceil($total / $per_page) 算总页数没问题,但必须确保 $total 是查 COUNT(*) 实时拿到的,不能缓存太久

















