如何檢查Java中兩個數字相乘是否會導致溢出
1. 概述
在 Java 中,如果結果超出資料類型( int
或long
)的限制,則兩個數字相乘可能會導致溢位。 Java 8 引入了Math.multiplyExact()
,自動偵測溢位並拋出ArithmeticException
。然而,在 Java 8 之前,需要手動方法來檢查溢位。
本文討論使用Math.multiplyExact()
現代方法和在兩個數字相乘時檢測溢位的原始方法。
2. 使用Math.multiplyExact()
進行溢位檢測
Java 8 引入了Math.multiplyExact()
方法,該方法檢查乘法期間是否有溢出。如果發生溢出,則會拋出ArithmeticException
。此方法同時支援int
和long
類型。
以下是我們如何使用Math.multiplyExact()
檢查int
和long
值是否溢位:
public class OverflowCheck {
public static boolean checkMultiplication(int a, int b) {
try {
Math.multiplyExact(a, b);
return true; // No overflow
} catch (ArithmeticException e) {
return false; // Overflow occurred
}
}
public static boolean checkMultiplication(long a, long b) {
try {
Math.multiplyExact(a, b);
return true; // No overflow
} catch (ArithmeticException e) {
return false; // Overflow occurred
}
}
}
在上面的範例中,我們建立了兩個方法: checkMultiplication(int a, int b)
和checkMultiplication(long a, long b)
。第一個方法檢查兩個整數相乘時是否有溢出,第二個方法檢查long
整數值是否溢出。
兩種方法都會捕捉ArithmeticException
並傳回boolean
,其中true
表示乘法成功(沒有溢位),否則false
。
讓我們檢查一個單元測試,其中包括int
和long
的各種乘法場景,解決有和沒有溢出的情況:
@Test
public void givenVariousInputs_whenCheckingForOverflow_thenOverflowIsDetectedCorrectly() {
// Int tests
assertTrue(OverflowCheck.checkMultiplication(2, 3)); // No overflow
assertFalse(OverflowCheck.checkMultiplication(Integer.MAX_VALUE, 3_000)); // Overflow
assertTrue(OverflowCheck.checkMultiplication(100, -200)); // No overflow (positive * negative)
assertFalse(OverflowCheck.checkMultiplication(Integer.MIN_VALUE, -2)); // Overflow (negative * negative)
assertTrue(OverflowCheck.checkMultiplication(-100, -200)); // No overflow (small negative values)
assertTrue(OverflowCheck.checkMultiplication(0, 1000)); // No overflow (multiplying with zero)
// Long tests
assertTrue(OverflowCheck.checkMultiplication(1_000_000_000L, 10_000_000L)); // No overflow
assertFalse(OverflowCheck.checkMultiplication(Long.MAX_VALUE, 2L)); // Overflow
assertTrue(OverflowCheck.checkMultiplication(1_000_000_000L, -10_000L)); // No overflow (positive * negative)
assertFalse(OverflowCheck.checkMultiplication(Long.MIN_VALUE, -2L)); // Overflow (negative * negative)
assertTrue(OverflowCheck.checkMultiplication(-1_000_000L, -10_000L)); // No overflow (small negative values)
assertTrue(OverflowCheck.checkMultiplication(0L, 1000L)); // No overflow (multiplying with zero)
}
在上面的單元測試中,我們首先驗證小正整數 (2 * 3) 和大值 (10 億 * 1000 萬) 的乘法分別保持在int
和long
範圍內,而不會導致溢出。
將Integer.MAX_VALUE
乘以 3.000 並將Long.MAX_VALUE
乘以 2 會正確觸發溢出,因為兩者都超出了其類型限制。
混合符號乘法,例如 100 * -200 和 10 億 * -10,000,不會導致溢出。對於負值,將Integer.MIN_VALUE
乘以 -2 並將Long.MIN_VALUE
乘以-2 會導致溢出,而較小的負值(如-100 * -200 和-1,000,000 * -10,000)則不會。
最後,將任何值乘以 0 總是可以避免溢出。
這些案例共同驗證了checkMultiplication()
方法能夠準確檢測int
和long
類型的安全且容易溢位的乘法。
3. 溢出檢測的原始方法
在 Java 8 之前,我們需要手動偵測乘法期間的溢出,因為沒有像Math.multiplyExact()
這樣的內建方法。總體想法是透過重新排列乘法邏輯來驗證兩個數字相乘是否會超出資料類型的界限。我們透過將操作數與資料類型的最大和最小可能值進行比較來做到這一點。
以下是我們如何使用int
和long
值的原始方法檢查溢位:
public class PrimitiveOverflowCheck {
public static boolean willOverflow(int a, int b) {
if (a == 0 || b == 0) return false;
if (a > 0 && b > 0 && a > Integer.MAX_VALUE / b) return true;
if (a > 0 && b < 0 && b < Integer.MIN_VALUE / a) return true;
if (a < 0 && b > 0 && a < Integer.MIN_VALUE / b) return true;
return a < 0 && b < 0 && a < Integer.MAX_VALUE / b;
}
public static boolean willOverflow(long a, long b) {
if (a == 0 || b == 0) return false;
if (a > 0 && b > 0 && a > Long.MAX_VALUE / b) return true;
if (a > 0 && b < 0 && b < Long.MIN_VALUE / a) return true;
if (a < 0 && b > 0 && a < Long.MIN_VALUE / b) return true;
return a < 0 && b < 0 && a < Long.MAX_VALUE / b;
}
}
willOverflow(int a, int b)
方法檢查兩個int
值相乘是否會超出int
類型的最大值或最小值。它考慮操作數為正或負的情況以及它們的乘積是否超出範圍。
類似地,對於long
值, willOverflow(long a, long b)
方法會執行檢查以確保它們的乘法不會超出long
類型的界限。
此方法的關鍵組成部分是最終檢查,它專門解決兩個值均為負數的情況。此檢查至關重要,因為如果沒有它,我們可能會忽略兩個負數相乘產生超出資料類型允許的最大值的正結果的情況。發生這種情況的原因是,在整數溢位的情況下,兩個大負數可能會產生超出int
值允許範圍的正結果。
兩種方法都使用類似的方法來偵測溢出,透過將運算元與其各自類型的最大值和最小值進行比較,確保乘法結果保持在安全範圍內。
這是一個簡單的單元測試,涵蓋了許多可能的溢出情況:
@Test
public void givenVariousInputs_whenCheckingForOverflow_thenOverflowIsDetectedCorrectly() {
// Int tests
assertFalse(PrimitiveOverflowCheck.willOverflow(2, 3)); // No overflow
assertTrue(PrimitiveOverflowCheck.willOverflow(Integer.MAX_VALUE, 3_000)); // Overflow
assertFalse(PrimitiveOverflowCheck.willOverflow(100, -200)); // No overflow (positive * negative)
assertTrue(PrimitiveOverflowCheck.willOverflow(Integer.MIN_VALUE, -2)); // Overflow (negative * negative)
assertFalse(PrimitiveOverflowCheck.willOverflow(-100, -200)); // No overflow (small negative values)
assertFalse(PrimitiveOverflowCheck.willOverflow(0, 1000)); // No overflow (multiplying with zero)
// Long tests
assertFalse(PrimitiveOverflowCheck.willOverflow(1_000_000_000L, 10_000_000L)); // No overflow
assertTrue(PrimitiveOverflowCheck.willOverflow(Long.MAX_VALUE, 2L)); // Overflow
assertFalse(PrimitiveOverflowCheck.willOverflow(1_000_000_000L, -10_000L)); // No overflow (positive * negative)
assertTrue(PrimitiveOverflowCheck.willOverflow(Long.MIN_VALUE, -2L)); // Overflow (negative * negative)
assertFalse(PrimitiveOverflowCheck.willOverflow(-1_000_000L, -10_000L)); // No overflow (small negative values)
assertFalse(PrimitiveOverflowCheck.willOverflow(0L, 1000L)); // No overflow (multiplying with zero)
}
此測試與先前的測試類似,確認willOverflow()
正確識別int
和long
類型的溢出條件。它確保該方法準確檢測乘法運算何時溢出,與Math.multiplyExact()
的行為保持一致。
4. 結論
檢測乘法期間的溢出對於避免意外結果至關重要。 Java 8 的Math.multiplyExact()
提供了一個簡單的內建方法,透過拋出ArithmeticException
來檢查溢位。但是,在早期版本的 Java 中或對於特定用例,也可以使用手動(原始)方法來檢測溢位。兩種方法都可以安全地處理算術運算,而不會產生無效結果。
與往常一樣,原始碼可以 在 GitHub 上取得。