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, не бяха добавени и двата и ето защо. Опитах и ​​плаващи, и двойни със същия резултат.

Но защо?


person jowie    schedule 18.06.2014    source източник
comment
Разбирате ли концепцията за hash?   -  person Sulthan    schedule 18.06.2014
comment
Доколкото ми е известно, това е цяло число без знак, което (предполагам) предоставя уникална препратка към обект с конкретна стойност... Но ако пропускам нещо, моля, уведомете ме.   -  person jowie    schedule 18.06.2014
comment
Да, пропускате нещо, hash не гарантира уникалност. Това е като пощенски код. Много хора ще споделят един и същ пощенски код, но пощенските кодове все още са голяма помощ, когато търсите някого. hash нямате смисъл без, ако нямате и добър isEqual.   -  person Sulthan    schedule 18.06.2014
comment
wikipedia: Хеш функция   -  person zaph    schedule 18.06.2014
comment
Да, сега го осъзнавам. Просто бях изненадан, че хешът на NSNumber връща само стойност въз основа на неговото unsignedInteger. Освен това не очаквах поведението на устройството да е различно от това на симулатора.   -  person jowie    schedule 18.06.2014


Отговори (2)


Мисля, че тази отлична статия от Майк Аш може да даде известна представа:

За плаващи числа, които са цели числа, искаме да направим същото. Тъй като нашият isEqual: счита DOUBLE с цяло число за равен на INT или UINT със същата стойност, трябва да върнем същия хеш като еквивалента на INT и UINT. За да постигнем това, ние проверяваме дали стойността DOUBLE действително е цяло число и връщаме целочислената стойност, ако е така:

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

(Няма да цитирам целия раздел за hash, така че, моля, прочетете статията за пълно разкриване).

Но в крайна сметка изглежда, че използването на [NSNumber hash] е лоша идея като ключ в асоциативен масив/хеш таблица. Въпреки това не мога да обясня защо се държи по различен начин под симулатора и устройството; това изглежда малко притеснително...

person trojanfoe    schedule 18.06.2014
comment
Благодаря... Но как мога да напиша хеша на моя обект с NSNumber предвид, така че двата обекта да са уникални? - person jowie; 18.06.2014
comment
Освен това не съм загрижен за isEqual:, тъй като това всъщност връща правилния резултат. Това е хешът, който не работи. Забелязах също, че хешовете на NSNumbers между 0,5 и 0,9 всички връщат същата стойност. - person jowie; 18.06.2014
comment
@jowie Но само на устройството? На симулатора изглежда, че работи? - person trojanfoe; 18.06.2014
comment
да - само на устройството. За съжаление това е начинът, по който има значение! - person jowie; 18.06.2014
comment
@jowie Да, това е тревожно. Apple препоръчва използването на низови стойности за ключове, за да можете да опитате това: developer.apple.com/library/ios/documentation/cocoa/conceptual/ - person trojanfoe; 18.06.2014
comment
Освен това преминах от замяна на isEqual: за сравняване на хешове към извършване на правилно сравняване на свойствата на обекта: stackoverflow.com/questions/254281/ - сега разбирам разликата. - person jowie; 19.06.2014

Няма гаранция, че хешът за различните входове е различен.

В този случай помислете, че има 2^32 хеш стойности и има по-уникални величини NSSNumbers, така че хешът не може да се използва за уникалност.

Доста кратък хеш обикновено се използва като бързо първоначално сравнение и след това, ако съвпадне, с пълно сравнение на обекта. Вероятно това прави NSNumber isEqual.

Ето защо използването на хеш като ключ в NSSet е лоша идея и поради причините, цитирани от @trojanfoe от Майк Аш, хешът NSNumber няма да работи.

Дори криптографските хешове като SHA512 не са гарантирани, че ще дадат различни резултати за различни входове, но шансът е малък, тъй като дължината на хеша се увеличава. Ето защо MD5 не се препоръчва и дори SHA2 все повече се смята за късо.

person zaph    schedule 18.06.2014