Почему хэш-значения различаются для NaN и Inf-Inf? - PullRequest
0 голосов
/ 08 января 2019

Я часто использую эту хеш-функцию, т.е. для записи значения кадра данных. Хотел посмотреть, смогу ли я сломать это. Почему эти хэш-значения не идентичны?

Для этого требуется пакет дайджеста.

Вывод простого текста:

> digest(Inf-Inf)
[1] "0d59b2dae9351c1ce6c76133295322d7"
> digest(NaN)
[1] "4e9653ddf814f0d16b72624aeb85bc20"
> digest(1)
[1] "6717f2823d3202449301145073ab8719"
> digest(1 + 0)
[1] "6717f2823d3202449301145073ab8719"
> digest(5)
[1] "5e338704a8e069ebd8b38ca71991cf94"
> digest(sum(1, 1, 1, 1, 1))
[1] "5e338704a8e069ebd8b38ca71991cf94"
> digest(1^0)
[1] "6717f2823d3202449301145073ab8719"
> 1^0
[1] 1
> digest(1)
[1] "6717f2823d3202449301145073ab8719"

Дополнительные странности. Вычисления, которые равны NaN, имеют идентичные хэш-значения, но хэш-значения NaN не эквивалентны:

> Inf - Inf
[1] NaN
> 0/0
[1] NaN
> digest(Inf - Inf)
[1] "0d59b2dae9351c1ce6c76133295322d7"
> digest(0/0)
[1] "0d59b2dae9351c1ce6c76133295322d7"
> digest(NaN)
[1] "4e9653ddf814f0d16b72624aeb85bc20"    

Ответы [ 2 ]

0 голосов
/ 08 января 2019

tl; dr это связано с очень глубокими подробностями того, как NaN s представлены в двоичном виде. Вы можете обойти это, используя digest(.,ascii=TRUE) ...

В продолжение ответа @ Jozef: обратите внимание на полужирные цифры ...

> base::serialize(Inf-Inf,connection=NULL)
[1] 58 0a 00 00 00 03 00 03 06 00 00 03 05 00 00 00 00 05 55 54 46 2d 38 00 00
[26] 00 0e 00 00 00 01 <strong>ff</strong> f8 00 00 00 00 00 00
> base::serialize(NaN,connection=NULL)
[1] 58 0a 00 00 00 03 00 03 06 00 00 03 05 00 00 00 00 05 55 54 46 2d 38 00 00
[26] 00 0e 00 00 00 01 <strong>7f</strong> f8 00 00 00 00 00 00

В качестве альтернативы, используя pryr::bytes() ...

> bytes(NaN)
[1] "7F F8 00 00 00 00 00 00"
> bytes(Inf-Inf)
[1] "FF F8 00 00 00 00 00 00"

Статья Википедии о формате с плавающей запятой / NaNs гласит:

Некоторые операции с арифметикой с плавающей точкой недопустимы, например, получение квадратного корня из отрицательного числа. Достижение недопустимого результата называется исключением с плавающей точкой. Исключительный результат представлен специальным кодом, называемым NaN, для «Не числа». Все NaN в IEEE 754-1985 имеют этот формат:

  • знак = 0 или 1.
  • смещенная экспонента = все 1 бит.
  • дробь = все, кроме всех 0 бит (поскольку все 0 бит представляют бесконечность).

Знак - первый бит; показатель степени - следующие 11 битов; дробь это последние 52 бита. При переводе первых четырех шестнадцатеричных цифр, приведенных выше, в двоичную форму, Inf-Inf равно 1111 1111 1111 0100 (знак = 1; экспонента - все единицы, как требуется; дробь начинается с 0100), тогда как NaN равно 0111 1111 1111 0100 (то же самое, но со знаком = 0).

Чтобы понять , почему Inf-Inf заканчивается знаковым битом 1, а NaN имеет знаковый бит 0, вам, вероятно, придется глубже изучить способ реализации арифметики с плавающей запятой на этой платформе. ..

Возможно, стоит поднять проблему в дайджесте репозитория GitHub по этому поводу; Я не могу придумать элегантный способ сделать это, но кажется разумным, что объекты, где identical(x,y) равен TRUE в R, должны иметь идентичные хэши ... Обратите внимание, что identical() специально игнорирует эти различия в битовых комбинациях с помощью аргумента single.NA (по умолчанию TRUE):

single.NA: логическое указание, если концептуально существует только один числовой ‘NA’ и один ‘NaN’; «Single.NA = FALSE» различает бит узоры.

В коде C похоже, что R просто использует оператор C != для сравнения NaN значений , если не включено побитовое сравнение, и в этом случае он явно проверяет равенство памяти места: см. здесь . То есть оператор сравнения C, по-видимому, обрабатывает различные типы значений NaN как эквивалентные ...

0 голосов
/ 08 января 2019

Это связано с digest::digest с использованием base::serialize, что дает неидентичные результаты для 2 упомянутых объектов с ascii = FALSE, который по умолчанию передается ему digest:

identical(
  base::serialize(Inf-Inf, connection = NULL, ascii = FALSE),
  base::serialize(NaN, connection = NULL, ascii = FALSE)
)
# [1] FALSE

Даже если

identical(Inf-Inf, NaN)
# [1] TRUE
...