2014-06-18 40 views
4

是否有人可以確認,並解釋,爲什麼發生這種情況:NSNumbers與值0.5和1.0具有相同的哈希

在模擬器(7.1,32位):

NSNumber *a = [NSNumber numberWithFloat:0.5]; // hash = 506952114 
NSNumber *b = [NSNumber numberWithFloat:1.0]; // hash = 2654435761 
NSNumber *c = [NSNumber numberWithFloat:2.0]; // hash = 1013904226 

在設備上(7.1 32位):

NSNumber *a = [NSNumber numberWithFloat:0.5]; // hash = 2654435761 
NSNumber *b = [NSNumber numberWithFloat:1.0]; // hash = 2654435761 - SAME! 
NSNumber *c = [NSNumber numberWithFloat:2.0]; // hash = 5308871522 

我想這可能是一個32位的問題,但是當我嘗試在64位模擬器和設備同樣的事情,我得到了同樣的問題。模擬器很好,設備具有相同的哈希。

我試圖給NSMutableOrderedSet添加獨特的對象,並注意到我的兩個對象除了0.5和1.0的不同值之外都是相同的,這兩個對象都沒有被添加,這就是原因。我嘗試了兩次漂浮和同樣的結果。

但是爲什麼?

+1

你明白'hash'的概念嗎? – Sulthan

+0

據我所知,這是一個無符號整數(我認爲)提供了一個具有特定值的對象的唯一引用...但如果我錯過了一些東西,請告訴我。 – jowie

+1

是的,你錯過了一些東西,'散列'並不能保證唯一性。這就像一個郵政編碼。很多人會分享相同的郵政編碼,但是當您搜索某人時,郵政編碼仍然是一個很大的幫助。如果你沒有一個好的'isEqual','hash'也沒有意義。 – Sulthan

回答

2

我覺得這excellent article邁克灰可能給一些見解:

對於那些整數值花車,我們想要做同樣的事情。 由於我們的isEqual:認爲整數值DOUBLE等於INT 或具有相同值的UINT,所以我們必須返回與INT和012相同的散列值。要做到這一點,我們檢查,看看是否DOUBLE 值實際上是一個整數,並返回整數值如果是這樣:

if(_value.d == floor(_value.d)) 
     return [self unsignedIntegerValue]; 

(我不會把整段約hash,所以請閱讀文章全面披露)。

但是,底線,它看起來像使用[NSNumber hash]作爲關聯數組/哈希表中的關鍵是一個壞主意。但是我無法解釋爲什麼它在模擬器和設備下的行爲不同。這看起來有點麻煩......

+0

謝謝......但是我怎樣才能用'NSNumber'來寫我的對象的散列,因此這兩個對象是唯一的呢? – jowie

+0

另外,我不關心'isEqual:',因爲這實際上帶來了正確的結果。這是散列不起作用。我也注意到,0.5和0.9 *之間的'NSNumber'散列值*都返回相同的值。 – jowie

+0

@jowie但只在設備上?在模擬器上它看起來像它的作品? – trojanfoe

2

不能保證不同輸入的散列不同。

在這種情況下,考慮有2^32個散列值,並且存在更大的唯一性NSSNumbers,因此散列不能用於唯一性。

一個相當短的散列通常用作快速初始比較然後如果它匹配完全比較的對象。這可能是NSNumberisEqual所做的。

這就是爲什麼在NSSet中使用散列作爲密鑰是一個壞主意,並且由於@When Ash引用了來自Mike Ash的NSNumber散列不起作用。

即使像SHA512這樣的加密散列也不能保證對不同的輸入產生不同的結果,但隨着散列長度的增加,機會很小。這就是爲什麼建議使用MD5,甚至SHA2越來越被認爲是短路。