【问题标题】:Murmurhash3 between Java and C++ is not aligningJava 和 C++ 之间的 Murmurhash3 没有对齐
【发布时间】:2014-06-06 19:59:03
【问题描述】:

我有 2 个独立的应用程序,一个是 Java,另一个是 C++。我同时使用 Murmurhash3。但是,在 C++ 中,对于相同的字符串,与 Java 相比,我得到了不同的结果

这是来自 C++ 的一个:https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp?r=144

我正在使用以下功能:

void MurmurHash3_x86_32 ( const void * key, int len,
                      uint32_t seed, void * out )

这是用于 Java 的:http://search-hadoop.com/c/HBase:hbase-common/src/main/java/org/apache/hadoop/hbase/util/MurmurHash3.java||server+void+%2522hash

上面的相同 Java 代码有多个版本。

这就是我调用 Java 的方式:

String s = new String("b2622f5e1310a0aa14b7f957fe4246fa");
System.out.println(MurmurHash3.murmurhash3_x86_32(s.getBytes(), 0, s.length(), 2147368987));

我从 Java 得到的输出: -1868221715

我从 C++ 得到的输出 3297211900

当我测试其他一些示例字符串时,例如 “7c6c5be91430a56187060e06fd64dcb8”和“7e7e5f2613d0a2a8c591f101fe8c7351”在 Java 和 C++ 中匹配。

欢迎指点

【问题讨论】:

  • 我对java不太了解,但我一直认为它的内部字符串字节是utf16
  • 我尝试了以下方法:s = new String("b2622f5e1310a0aa14b7f957fe4246fa".getBytes(), "UTF-16"); System.out.println(MurmurHash3.murmurhash3_x86_32(s.getBytes(), 0, s.length(), MURMUR_SEED));我仍然看到差异
  • 你可能想要输出你塞入 murmur 函数的大小和单个字节,看看它们是否相同
  • 内部 Java Strings 是 UTF-16 大端,但是当您调用 getBytes() 时,它会返回给您默认的平台编码。如果您没有显式设置它,请始终假定它是 EBCDIC 或同样可怕的东西,并指定显式编码。

标签: java c++ murmurhash


【解决方案1】:

我可以看到两个问题。首先,C++ 使用uint32_t,并给你一个值 3,297,211,900。这个数字比有符号的 32 位 int 大,Java 只使用有符号整数。但是,-1,868,221,715 不等于 3,297,211,900,即使考虑了有符号整数和无符号整数之间的差异。

(在 Java 8 中,他们添加了 Integer.toUnsignedString(int),它将有符号的 32 位 int 转换为其无符号字符串表示。在早期版本的 Java 中,您可以将 int 转换为 long,然后进行掩码关闭高位:((long) i) & 0xffffffffL。)

第二个问题是您使用了错误的getBytes() 版本。不带参数的那个使用默认平台编码将 Unicode String 转换为 byte[],这可能会因您的系统设置方式而异。它可能会为您提供 UTF-8、Latin1、Windows-1252、KOI8-R、Shift-JIS、EBCDIC 等。

在任何情况下都不要调用String.getBytes() 的无参数版本。它应该被弃用、淘汰、去窗、销毁和删除。

请改用s.getBytes("UTF-8")(或您希望获得的任何编码)。

正如Python 之禅所说,“显式胜于隐式。”

我不知道除了这两个之外是否还有其他问题。

【讨论】:

    【解决方案2】:

    我和你有同样的问题。但是我的 Murmurhash3 的 Java 版本与你的不同。在对 Murmurhash3 的 C++ 版本进行了一些更改后,我使两个版本生成的哈希值相同。我给你我的解决方案,你可以用它来检查它是否也适合你。

    也许 Java 和 C++ 版本之间最大的区别在于 右移操作(在 Java 中你可以看到>> 和 >>>,而在 C++ 中你只能看到 >>)。 Java 中的整数都是有符号的,而在 C++ 中,您可以使用有符号或无符号整数。在 Java 版本中,>> 表示算术右移,>>> 表示逻辑右移。而在 C++ 中,>> 表示算术右移。 Murmurhash3 的原始 C++ 版本使用无符号整数,为了像在 Java 中一样生成负哈希值,first 在 C++ 中您应该将所有无符号类型 uint32_t 更改为有符号输入 int32_t那么你应该在Java中找到>>>并在C++中围绕相应的>>进行更改。对我来说,我是从:

    inline uint32_t rotl32 ( uint32_t x, int8_t r )
    {
      return (x << r) | (x >> (32 - r));
    }
    

    收件人:

    inline int32_t rotl32 ( int32_t x, int8_t r )
    {
      return (x << r) | (int32_t)((uint32_t)x >> (32 - r)); //similar to >>> in Java
    }
    

    来自:

    FORCE_INLINE uint32_t fmix32 ( uint32_t h )
    {
      h ^= h >> 16;
      h *= 0x85ebca6b;
      h ^= h >> 13;
      h *= 0xc2b2ae35;
      h ^= h >> 16;
    
      return h;
    }
    

    收件人:

    FORCE_INLINE int32_t fmix32 ( int32_t h )
    {
      h ^= (int32_t)((uint32_t)h >> 16); // similar to >>> in Java
      h *= 0x85ebca6b;
      h ^= (int32_t)((uint32_t)h >> 13);
      h *= 0xc2b2ae35;
      h ^= (int32_t)((uint32_t)h >> 16);
    
      return h;
    }
    

    这样,我的两个版本的 Java 和 C++ 的 Murmurhash3 生成相同的哈希值。

    【讨论】:

    • 如何在不更改 C++ 代码的情况下解决 java 代码中的这个问题?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-05
    • 2011-02-18
    • 2018-09-07
    相关资源
    最近更新 更多