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 all возвращают одно и то же значение. - person jowie; 18.06.2014
comment
@jowie Но только на устройстве? На симуляторе вроде работает? - person trojanfoe; 18.06.2014
comment
да - только на устройстве. К сожалению, это имеет значение! - person jowie; 18.06.2014
comment
@jowie Да, это беспокоит. Apple рекомендует использовать строковые значения для ключей, чтобы вы могли попробовать это: noreferrer">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