修复MathUtil.multiple 方法在大整数乘法运算中整数溢出风险

This commit is contained in:
Looly
2025-12-09 15:50:23 +08:00
parent e2df8d44ad
commit 77fbea1548
2 changed files with 40 additions and 18 deletions

View File

@@ -339,7 +339,15 @@ public class MathUtil {
* @return 最小公倍数
*/
public static int multiple(final int m, final int n) {
return m * n / gcd(m, n);
// 先计算最大公约数
final int gcd = gcd(m, n);
// 使用长整型避免溢出,再转换回整型
final long result = (long) m / gcd * (long) n;
// 检查结果是否在int范围内
if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE) {
throw new ArithmeticException("Integer overflow: " + m + " * " + n + " / " + gcd);
}
return (int) result;
}
private static int mathSubNode(final int selectNum, final int minNum) {

View File

@@ -16,45 +16,59 @@
package cn.hutool.v7.core.math;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.math.BigInteger;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class MathUtilTest {
@Test
public void factorialTest(){
long factorial = MathUtil.factorial(0);
Assertions.assertEquals(1, factorial);
assertEquals(1, factorial);
Assertions.assertEquals(1L, MathUtil.factorial(1));
Assertions.assertEquals(1307674368000L, MathUtil.factorial(15));
Assertions.assertEquals(2432902008176640000L, MathUtil.factorial(20));
assertEquals(1L, MathUtil.factorial(1));
assertEquals(1307674368000L, MathUtil.factorial(15));
assertEquals(2432902008176640000L, MathUtil.factorial(20));
factorial = MathUtil.factorial(5, 0);
Assertions.assertEquals(120, factorial);
assertEquals(120, factorial);
factorial = MathUtil.factorial(5, 1);
Assertions.assertEquals(120, factorial);
assertEquals(120, factorial);
Assertions.assertEquals(5, MathUtil.factorial(5, 4));
Assertions.assertEquals(2432902008176640000L, MathUtil.factorial(20, 0));
assertEquals(5, MathUtil.factorial(5, 4));
assertEquals(2432902008176640000L, MathUtil.factorial(20, 0));
}
@Test
public void factorialTest2(){
long factorial = MathUtil.factorial(new BigInteger("0")).longValue();
Assertions.assertEquals(1, factorial);
assertEquals(1, factorial);
Assertions.assertEquals(1L, MathUtil.factorial(new BigInteger("1")).longValue());
Assertions.assertEquals(1307674368000L, MathUtil.factorial(new BigInteger("15")).longValue());
Assertions.assertEquals(2432902008176640000L, MathUtil.factorial(20));
assertEquals(1L, MathUtil.factorial(new BigInteger("1")).longValue());
assertEquals(1307674368000L, MathUtil.factorial(new BigInteger("15")).longValue());
assertEquals(2432902008176640000L, MathUtil.factorial(20));
factorial = MathUtil.factorial(new BigInteger("5"), new BigInteger("0")).longValue();
Assertions.assertEquals(120, factorial);
assertEquals(120, factorial);
factorial = MathUtil.factorial(new BigInteger("5"), BigInteger.ONE).longValue();
Assertions.assertEquals(120, factorial);
assertEquals(120, factorial);
Assertions.assertEquals(5, MathUtil.factorial(new BigInteger("5"), new BigInteger("4")).longValue());
Assertions.assertEquals(2432902008176640000L, MathUtil.factorial(new BigInteger("20"), BigInteger.ZERO).longValue());
assertEquals(5, MathUtil.factorial(new BigInteger("5"), new BigInteger("4")).longValue());
assertEquals(2432902008176640000L, MathUtil.factorial(new BigInteger("20"), BigInteger.ZERO).longValue());
}
@Test
public void testMultipleOverflow() {
final int a = 500000;
final int b = 600000;
// 原方法使用 a * b / gcd(a, b) 计算a * b 会先溢出,得到最小公倍数为负数
// 使用修改后的multiple方法测试它是否能正确处理这种情况
final int result = MathUtil.multiple(a, b);
// 验证结果必须是正数(两个正数的最小公倍数必须为正)
assertTrue(result > 0);
}
}