2009-02-04 67 views
8

我寫了一個方法,一個給定的數量從天轉換成毫秒:的Java乘法操作行爲

private long expireTimeInMilliseconds; 
... 
public void setExpireTimeInDays(int expireTimeInDays) 
{ 
    expireTimeInMilliseconds = expireTimeInDays * 24 * 60 * 60 * 1000; 
} 

我有一個很難弄清楚我做錯了什麼。現在我的問題: 這個錯誤是如此明顯嗎?

修正方法:

private long expireTimeInMilliseconds; 
... 
public void setExpireTimeInDays(int expireTimeInDays) 
{ 
    expireTimeInMilliseconds = ((long) expireTimeInDays) * 24 * 60 * 60 * 1000; 
} 

如果我沒有在整數運算之前轉換爲長,我得到一個完整的錯誤的結果。

+2

你也可以給常量附加一個L. – starblue 2009-02-04 17:19:36

+0

爲什麼人們想要關閉它?看起來像一個合理的問題,可能會幫助其他人。如果它是完全重複的,請說出來。 – 2009-02-04 17:40:23

+1

你也可以聲明你的常量:24L * 60L ... – 2009-02-04 20:01:09

回答

8

是否明顯?我想這取決於你使用Java的時間有多長以及你需要處理幾毫秒的次數。當然,它應該可以長達大約24天...

我認爲最大的提示應該是System.currentTimeMillis()返回long。這是一個很好的跡象表明幾毫秒可以變大。你正在設置的變量的類型也應該是一個很好的提示。

當然,你也有瞭解到,如果你使用整數進行算術運算,結果將是int與溢出環繞。是否足夠明顯可以辯論,但這將是一個毫無意義的討論。在C#中,如果你打開了溢出檢查,你很快就會發現這個錯誤 - 但是並不是很多開發者這樣做(事實上,儘管我可能應該這樣做)。

+0

我期待了很久,我習慣使用System.currentTimeMillis()。因爲這不是一個新代碼,所以我不記得當我編寫它時我在想什麼(可能是我期待編譯器有一些魔力)...... – 2009-02-04 21:06:22

2

不,它並不明顯。

但是請相信我,經過多年的練習並修復這樣的錯誤,你會對整數溢出變得非常敏感,並且在沒有考慮它的情況下做正確的事情。

這是每個人都會發生的事情。一定沒有錯誤的代碼實踐的跡象,無知等等。

+0

有趣的是,我已經編寫了20多年了,這是我第一次犯這個錯誤。當我是一名C/C++開發人員時,我經常關注代碼中的每個細節。我認爲,現在我期待編譯器爲我做一些「魔術」...... – 2009-02-04 20:33:29

7

是的,這很明顯,如果你以前做過。每當你看到一串數字相乘時,你應該自動開始考慮整數溢出錯誤。在這種情況下,如果expireTimeInDays大於24,則設置爲溢出。從技術上講,你應該考慮溢出錯誤任何時候使用整數,但將它們乘以這樣的一組應該是一個非常大的紅旗。

3

您的操作數變量和文字數字的類型爲int。 int數據類型的最大值爲2^31 -1。因此,如此大的數字,int的數據類型溢出導致看起來不正確的答案。

在你的第一個例子中,int只會被提升爲一個長的賦值變量,在之後進行計算。計算結果是一個整數。

第二個示例將第一個操作數強制轉換爲long,導致計算的提升變長。在這種情況下,由於升級,計算結果很長。長數據類型對於您的計算而言足夠大。

3

您可能有興趣知道這是由Joshua Bloch和Neal Gafter撰寫的「Java Puzzlers」中介紹的。

alt text http://www.javapuzzlers.com/lg-puzzlers-cropped.jpg

你會發現許多其他的Java陷阱,陷阱和角落的情況下在這本書中。

我同意留下評論的starblue。追加一個L到數字。

1

只是添加到其他答案,我發現它在過去定義常量(public static final long),如MILLISECS_DAYMILLISECS_HOUR有幫助。 更具可讀性和實用性。

0

我並不想證明我的錯誤,但如果java編譯器足夠聰明,可以在計算之前將int提升到很長時間(一旦將計算分配給long類型的變量)順便說一句,我曾經使用過C/C++,如果它是一個C程序,我也遇到了同樣的問題,但幾年前我對這種操作更加小心。

我會更加關注下一次(或切換到蟒蛇)...:d

1

另一種方式來寫,這是

public void setExpireTimeInDays(int expireTimeInDays) 
{ 
    expireTimeInMilliseconds = (long) expireTimeInDays * 24 * 60 * 60 * 1000; 
} 

public void setExpireTimeInDays(int expireTimeInDays) 
{ 
    expireTimeInMilliseconds = expireTimeInDays * 24L * 60 * 60 * 1000; 
} 
1

如果您在你的代碼上使用FindBugs,它會檢測到這個確切的問題。 「ICAST:整數乘法的結果變爲long。」 FindBugs的例子正是你在做的事情;計算以毫秒爲單位的天數。

第一次遇到這個問題對我來說並不明顯。

1

有一些靜態分析工具(findbugs)會發現這些類型的錯誤。

計算機上的數值計算可能很難。操作事項的順序可能會影響精確度和準確度,這是您不希望的。日期數學也可能令人驚訝地棘手。通常最好使用日期/日曆例程,而不是嘗試自己進行數學運算,但這些例程並不是java類庫中設計最好的例程。