为什么NaN和Inf-Inf的哈希值不同?

Kin*_*lia 21 math hash r digest

我经常使用这个哈希函数,即记录数据帧的值.想看看我是否能打破它.为什么这些哈希值不相同?

这需要摘要包.

纯文本输出:

> 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"
Run Code Online (Sandbox Code Playgroud)

额外的怪异.等于NaN的计算具有相同的哈希值,但NaN的哈希值不等效:

> Inf - Inf
[1] NaN
> 0/0
[1] NaN
> digest(Inf - Inf)
[1] "0d59b2dae9351c1ce6c76133295322d7"
> digest(0/0)
[1] "0d59b2dae9351c1ce6c76133295322d7"
> digest(NaN)
[1] "4e9653ddf814f0d16b72624aeb85bc20"    
Run Code Online (Sandbox Code Playgroud)

Ben*_*ker 29

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 ff 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 7f 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"
Run Code Online (Sandbox Code Playgroud)

浮点格式的维基百科文章/ NaN的说:

某些浮点运算操作无效,例如取负数的平方根.达到无效结果的行为称为浮点异常.特殊结果由称为NaN的特殊代码表示,用于"非数字".IEEE 754-1985中的所有NaN都具有以下格式:

  • sign = 0或1.
  • 偏置指数=全1位.
  • 分数=除了所有0位以外的任何值(因为所有0位代表无穷大).

标志是第一位; 指数是接下来的11位; 分数是最后52位.平移上面给出为二进制的前四个十六进制数字,Inf-Inf1111 1111 1111 0100(符号= 1;指数是所有的,根据需要;馏分开始0100),而NaN0111 1111 1111 0100(相同的,但带符号= 0).

要理解为什么 Inf-Inf最终得到符号位1并且NaN符号位为0,您可能需要更深入地研究在该平台上实现浮点运算的方式......

可能值得在GitHub摘要上提出一个关于此问题的问题 ; 我想不出一个优雅的方式做到这一点,但它似乎是合理的对象,其中identical(x,y)TRUER中应具有相同的哈希值...请注意,identical()特别是忽略了通过在位模式这些差异single.NA(默认TRUE)的说法:

single.NA:逻辑表示概念上是否只有一个数字'NA'和一个'NaN'; 'single.NA = FALSE'区分位模式.

在C代码中,看起来R只是简单地使用C的!=运算符来比较NaN值,除非启用了按位比较,在这种情况下它会显式检查内存位置的相等性:请参见此处.也就是说,C的比较运算符似乎将不同类型的NaN值视为等价的......

  • 负无穷大明显大于正无穷大,结果为负.毕竟,任何2的恭维存储数字都比正值多一个负值.这是科学. (4认同)
  • @corsiKa [IEEE 754浮点数](https://en.wikipedia.org/wiki/IEEE_754#Formats)数字不是2的补码!它们更像是符号量级,在负数和正数上具有相等的范围.你可以很容易地看到这一点,因为浮点数同时知道+0和-0. (4认同)
  • 有一个很好的故事漂浮在一个程序员身上,他改变了一些C代码,将两个浮点数元素逐元素地与两个数组的简单`memcmp`进行比较.它通过了所有单元测试,但对于一个关键客户来说却非常糟糕.有+ 0/-0的东西,NaN!= NaN的东西,依此类推. (3认同)

Joz*_*zef 8

这与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
Run Code Online (Sandbox Code Playgroud)

即使

identical(Inf-Inf, NaN)
# [1] TRUE
Run Code Online (Sandbox Code Playgroud)