java int 和 long比较大小会_解析java的addExact()与multiplyExact()
java的加法在數字過大時是會溢出的,今天就遇到一個問題,要實現一個溢出時會拋出異常的加法函數。
這個問題細細一想卻是復雜的很:不僅要考慮加法,輸入參數還可以是負數反向溢出。好在java已經實現了該功能,直接閱讀Math庫的源碼。
public static long addExact(long x, long y) {long r = x + y;// HD 2-12 Overflow iff both arguments have the opposite sign of the resultif (((x ^ r) & (y ^ r)) < 0) {throw new ArithmeticException("long overflow");}return r; }這個寫法相當巧妙,首先我們知道java的int型在計算機中作為補碼存儲,而第一位是符號位(有時候會涉及到大小端存儲,此處不影響敘述),直接決定了數字的正負。整個代碼表面上是在對整個int做處理,實際有用的只有x y r的符號位而已。(0:正號 1:負號)下面列出x y r的符號位的真值表:
根據真值表反推,溢出只有“兩個負數相加變成正數“和”兩個正數相加變成負數“兩種情況。考慮值域后確實如此。
在學習addExact()時,我意外收獲了另一份有趣的代碼,就是multiplyExact()。
public static int multiplyExact(int x, int y) {long r = (long)x * (long)y;if ((int)r != r) {throw new ArithmeticException("integer overflow");}return (int)r; }public static long multiplyExact(long x, long y) {long r = x * y;long ax = Math.abs(x);long ay = Math.abs(y);if (((ax | ay) >>> 31 != 0)) {// Some bits greater than 2^31 that might cause overflow// Check the result using the divide operator// and check for the special case of Long.MIN_VALUE * -1if (((y != 0) && (r / y != x)) ||(x == Long.MIN_VALUE && y == -1)) {throw new ArithmeticException("long overflow");}}return r; }int型的轉成了long做暴力判斷,而long就沒辦法了。
首先用((ax | ay) >>> 31 != 0)做了一個判斷,等價于ax>2^31 || ay > 2^31。做這個判斷一方面是為了排除不會溢出的情況,另一方面也暗示接下來的步驟比較費時。
果然接下來直接用除法做了逆運算。。這個思路倒是非常粗暴有效,但是其中的額外情況 x == Long.MIN_VALUE && y == -1 也算是一個非常微妙的漏洞。由于溢出,這個式子相乘的結果是Long.MAXVALUE+1,也就是Long.MINVALUE。但是,除法也溢出成了Long.MAXVALUE+1,還是Long.MINVALUE,導致變成了需要額外判斷的特例。
標準庫都是一群大牛精簡出來的代碼,因為一個特例導致多了這樣一行代碼,cpu周期多走了幾個。想到他們殫精竭慮也沒法把這個特例從代碼中消除的樣子,可以說是十分有趣了。
總結
以上是生活随笔為你收集整理的java int 和 long比较大小会_解析java的addExact()与multiplyExact()的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux密码带星号,Linux下实现输
- 下一篇: java借口案例实现_java实现接口的