[英]Java not deterministic?
我在Java中編寫了一些捕食者 - 獵物模擬。 即使規則非常復雜並且最終處於混亂系統中,所使用的技術也很簡單:
所以我認為在用相同的參數初始化系統時它應該輸出相同的結果,但它沒有,我想知道為什么。
關於這一點的一些想法:我的應用程序使用Random
s,但是對於該測試,我使用給定值初始化它們,因此在我的理解中,它們應該為每次運行創建相同順序的相同輸出。
我正在迭代Set
s,我知道沒有定義迭代Set
的順序。 但是我沒有看到任何理由為什么以相同的值填充相同順序的Set
在多次運行中應該表現不同。 可以?
我正在使用很多float
。 1 + 1 = 1.9999999999725的數據類型總是讓我懷疑,但即使他們的行為對我來說很奇怪,也應該總是一樣奇怪。 不是嗎?
垃圾收集不是確定性的,但只要我不依賴於析構函數,我就應該是安全的。
如上所述,根據實際使用時間,沒有並發性和數據類型。
我不能在一個簡單的例子中重現這種行為。 但是通過我的代碼,我看不到任何可能無法預測的事情。 那么我上面的任何假設都是錯誤的嗎? 我有什么想法可以錯過嗎?
這是驗證我的假設的測試:
public static void main(String[] args) {
Random r = new Random(1);
Set<Float> s = new HashSet<Float>();
for (int i = 0; i < 1000000; i++) {
s.add(r.nextFloat());
}
float ret = 1;
int cnt = 0;
for (Float f : s) {
float multiply = 0.3f;
if (cnt++ % 2 == 0) {
multiply = 0.7f;
}
float f2 = (f * multiply);
ret += f2;
}
System.out.println(ret);
}
它對我來說總是在242455.25。
您可以用Java編寫確定性程序。 你只需要消除非確定性的可能來源。
如果沒有看到你的實際代碼,那么很難知道什么可能導致非決定論,以及這種決定論的具體證據。
有許多庫方法可能是非確定性行為的來源......取決於您如何使用它們。
例如, Object.hashcode()
返回的值(第一次在實例上調用)是非確定性的。 並且滲透到任何使用散列的庫。 它肯定會影響迭代它們時返回HashSet
或HashMap
元素的順序...如果元素類不覆蓋hashcode()
。
隨機數生成器可能是也可能不是確定性的。 如果它們是偽隨機的並且用固定種子初始化,則每個生成的數字序列將是確定性的。
浮點運算應該是確定性的。 對於算術表達式的任何(固定)輸入集,結果應始終相同。 (我不確定浮點運算的確定性是否由JLS保證,但是如果它在實踐中發生的話會很奇怪。就像......你在破壞的硬件上運行。)
關注 ...關於strictfp
和非確定性。
根據JLS 15.4 :
“在一個非FP嚴格的表達式中,為實現使用擴展指數范圍來表示中間結果的一些余地被授予;大致說來,凈效應是計算可能在以下情況下產生”正確答案“獨占使用浮點值設置或雙值設置可能會導致上溢或下溢。“
這並不能說明實現在非FP嚴格表達式中有多少 “余地”。 但是,我認為這種余地不會擴展到允許非確定性行為。 我曾經認為特定平台上的JIT編譯器總是會為同一個表達式生成等效的本機代碼,並且該代碼將是確定性的。 (我看不出任何非確定性的原因......除非硬件本身具有非確定性浮點。)非確定性的另一個可能來源可能是JIT編譯和解釋代碼的行為可能不同。 但坦率地說,我認為允許這種情況發生是“堅果”......我想我們已經聽說過了。
因此,雖然非FP嚴格的表達評估在理論上可能是非確定性的,但我認為我們應該對此進行折扣......除非有明確證據表明它在實踐中發生。
(請注意,我說的是真正的非決定論,而不是平台差異。)
我正在迭代通過集合,我知道迭代的集合的順序沒有被定義。 但是我沒有看到為什么以相同的值填充相同順序的Set在多次運行中應該表現不同的原因。 可以?
它可以。 該實現可以自由使用,例如,對象在內存中的位置作為底層哈希表的鍵。 這取決於垃圾收集何時運行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.