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

时间:2019-01-08 16:03:58

标签: r math hash digest

我经常使用此哈希函数,即记录数据帧的值。想看看我能否打破它。这些哈希值为何不相同?

这需要摘要包。

纯文本输出:

  checkMark = (id) => {
    this.setState(oldState => {
      const newTodo = oldState.todos.map(x => {
        if (x.id === id) {
          x.completed = !x.completed
          return x
        }
        return x
      })
      return {
        todos: newTodo
      }
    })
  }

其他怪异。等于NaN的计算具有相同的哈希值,但NaN的哈希值不相等:

> 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"

2 个答案:

答案 0 :(得分:30)

tl; dr 这与NaN如何用二进制表示的非常详细的细节有关。您可以使用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"

Wikipedia article on floating point format/NaNs说:

  

某些浮点算术运算无效,例如采用负数的平方根。达到无效结果的行为称为浮点异常。特殊结果表示为特殊结果,称为“非数字”的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,则可能必须更深入地研究浮点运算的方式在此平台上实现的...

可能值得为此提出一个issue on the digest GitHub repo;我想不出一种优雅的方法,但是在R中identical(x,y)TRUE的对象应该具有相同的哈希值似乎是合理的……注意{ {1}}通过identical()(默认single.NA)自变量特别忽略了位模式的这些差异:

  

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

在C代码中,看起来R只是使用C的TRUE运算符比较!=除非启用按位比较,在这种情况下,它将进行显式检查相等的存储位置:请参见here。也就是说,C的比较运算符似乎将不同类型的NaN值视为等效值...

答案 1 :(得分:8)

这与使用digest::digest的{​​{1}}有关,它为使用base::serialize的两个提到的对象提供了不同的结果,这是{{1}传递给它的默认结果}:

ascii = FALSE

尽管

digest