一、方法定义

Math.floorMod() 是 Java 标准库 java.lang.Math 类中的一个静态方法,用于执行向下取整的模运算(floor modulus),返回结果始终为非负数或零。

方法签名:

public static int floorMod(int x, int y)
public static long floorMod(long x, long y)
  • 参数
    • x:被除数(dividend)
    • y:除数(divisor),不能为 0
  • 返回值
    • 返回 xy 的“向下取整模运算”结果,类型为 intlong
  • 异常
    • ArithmeticException:当 y == 0 时抛出

二、功能说明

floorMod 的核心目标是解决传统 % 运算符在处理负数时结果符号不确定的问题。

% 运算符的关键区别:

运算方式 行为
% (remainder) 结果的符号与被除数(x)相同
floorMod 结果始终 ≥ 0(当 y > 0 时)

数学定义:

floorMod(x, y) 等价于:

x - (floorDiv(x, y) * y)

其中 floorDiv(x, y) 是向下取整的除法(Math.floorDiv(x, y))。

✅ 最终结果满足:0 <= floorMod(x, y) < |y|


三、示例代码

1. 基本用法对比 %floorMod

public class FloorModExample {
    public static void main(String[] args) {
        int divisor = 5;

        // 正数情况:两者结果相同
        System.out.println("13 % 5 = " + (13 % 5));           // 3
        System.out.println("Math.floorMod(13, 5) = " + Math.floorMod(13, 5)); // 3

        // 负数被除数:关键差异!
        System.out.println("-13 % 5 = " + (-13 % 5));         // -3
        System.out.println("Math.floorMod(-13, 5) = " + Math.floorMod(-13, 5)); // 2

        // 负除数
        System.out.println("13 % -5 = " + (13 % -5));         // 3
        System.out.println("Math.floorMod(13, -5) = " + Math.floorMod(13, -5)); // -2

        // 双负数
        System.out.println("-13 % -5 = " + (-13 % -5));       // -3
        System.out.println("Math.floorMod(-13, -5) = " + Math.floorMod(-13, -5)); // -3
    }
}

输出结果:

13 % 5 = 3
Math.floorMod(13, 5) = 3
-13 % 5 = -3
Math.floorMod(-13, 5) = 2
13 % -5 = 3
Math.floorMod(13, -5) = -2
-13 % -5 = -3
Math.floorMod(-13, -5) = -3

🔍 重点:当 y > 0 时,floorMod 结果始终在 [0, y) 范围内,非常适合数组索引、循环周期等场景。


四、使用技巧

1. 安全的数组索引循环(环形缓冲区)

int[] buffer = new int[5];
int index = -1; // 用户输入或计算可能为负

// 安全获取循环索引
int safeIndex = Math.floorMod(index, buffer.length); // 若 index=-1, 结果为 4
buffer[safeIndex] = 100;

2. 时间计算(如计算星期几)

// 假设今天是星期三(0=周日, 1=周一, ..., 6=周六)
int today = 3;
int daysAgo = 10;

// 计算 10 天前是星期几
int dayOfWeek = Math.floorMod(today - daysAgo, 7);
System.out.println("10天前是星期" + dayOfWeek); // 输出: 5 (周五)

3. 哈希环或一致性哈希中的节点选择

int nodeCount = 8;
int hashValue = -123456; // 哈希值可能为负

int nodeIndex = Math.floorMod(hashValue, nodeCount);
System.out.println("分配到节点: " + nodeIndex); // 保证在 [0,7] 范围内

五、常见错误

❌ 错误1:认为 floorMod 总是返回正数

System.out.println(Math.floorMod(13, -5)); // 输出: -2(负数!)

✅ 规则:结果符号与除数 y 相同。只有当 y > 0 时,结果才 ≥ 0。

❌ 错误2:除数为 0

Math.floorMod(10, 0); // 抛出 ArithmeticException: / by zero

✅ 必须确保 y != 0

❌ 错误3:混淆 floorMod%

// 错误假设
if (n % 5 == 3) { ... }
// 与
if (Math.floorMod(n, 5) == 3) { ... }
// 当 n 为负数时,行为不同!

六、注意事项

项目 说明
结果范围 0 <= r < |y|y > 00 >= r > -|y|y < 0
性能 % 略慢(因额外计算),但差异极小
浮点数不支持 double/float 版本
溢出情况 极少见,如 floorMod(Integer.MIN_VALUE, -1) 可能溢出
与 floorDiv 关系 x == floorDiv(x,y)*y + floorMod(x,y) 恒成立

七、最佳实践

  1. 周期性计算优先使用:如数组索引、时间计算、循环调度等。
  2. 替代负数取模的复杂逻辑:避免手动写 (x % y + y) % y
  3. 明确意图:使用 floorMod 表明你需要“非负余数”。
  4. 配合 floorDiv:需要向下取整除法时,两者成对使用。

✅ 推荐替代写法:

// 旧写法(易错)
int mod = ((x % y) + y) % y;

// 新写法(清晰安全)
int mod = Math.floorMod(x, y);

八、性能优化建议

场景 建议
确定 x ≥ 0 直接使用 x % y,性能更优
高频调用 floorMod 性能良好,通常无需优化
常量除数 JIT 可能优化,无需手动展开
浮点需求 使用 Math.IEEEremainder() 或自定义逻辑

九、总结

项目 内容
核心功能 实现向下取整模运算,解决负数取模符号问题
关键优势 y > 0 时,结果始终在 [0, y) 范围内
适用场景 数组索引、周期计算、哈希分配、时间逻辑
% 区别 % 是余数运算,符号随被除数;floorMod 是数学模运算
最佳实践 用于需要非负余数的场景,替代 (x%y+y)%y 冗余写法
注意事项 除数不能为 0;结果符号与除数相同

一句话掌握
Math.floorMod(x, y) 返回 x 除以 y非负余数(当 y > 0 时),是处理循环、索引等场景的安全首选