Java Puzzlers 之Puzzle 1: Oddity

Puzzle 1: Oddity

下面的方法就是用來判斷唯一的一個參數是否爲奇數,這個方法生效嗎?

 

}

 

Solution 1: Oddity

可以這樣來判斷奇數:一個整數被2除後餘數爲1。表達式i%2就是計算i2除後的餘數,所以這個程序似乎看起來應該生效。不幸的是,它沒有;它有四分之一的時間返回了錯誤的答案。

爲什麼是四分之一? 因爲一半的int值是負數,所以isOdd方法對所有的負奇數判斷失敗。對於任何負數,甚至爲奇數時,它也始終返回false。這是java對於取餘操作符(%)的邏輯定義。它按照以下的恆等式來定義任何inta和任何非零intb

 

(a / b) * b + (a % b) == a

 

換句話說,如果你用b來除a,再用b來乘以商,然後加上ab的餘數,你就能得到原來的數a。這個等式說明的很清楚,但是結合Java截取整型除法運算來看,它暗示了當取餘操作返回一個非零的結果時,它同操作符左邊的數具有同樣的符號。

isOdd方法以及對於奇數的定義都是在假設所有的餘數爲正數的基礎上的。儘管這種假設很符合某些形式的除法,但是Java取餘操作符是完全與整型除法運算一致,它拋棄了結果的小數部分。

如果i是一個負奇數,i%2就等於-1而不是1,因此isOdd方法就會錯誤的返回false,相反地比較如下:

 

public static boolean isOdd(int i) {

 

    return i % 2 != 0;

 

}

 

如果你在把isOdd方法使用在一個臨界的設置中,你最好使用按位AND操作符(&)來代替取餘操作符:

 

public static boolean isOdd(int i) {

    return (i & 1) != 0;

}

 

第二個版本中可能運行的比第一個版本快,這得依賴於運行的平臺和你使用的虛擬機,但是不可能運行的更慢。總的來說,除法和取餘操作要比其他算術和邏輯運算慢。過早的優化並不是個好主意,但是在上面這個情形下,運行較快的版本是很清晰明白的,沒有理由更喜歡這種原始的計算。

總之,在你使用取餘操作時,你總是得思考操作數的符號以及計算的結果。當操作數爲非負數時,這個很明顯,但是當其中一個或者兩個操作數都是負數時,就不是那麼明顯了。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章