【问题标题】:Bit masking unsigned and signed values位掩码无符号和有符号值
【发布时间】:2016-03-30 13:44:51
【问题描述】:

provided here为例,

我们创建了一个 64 位 ID,其中包含 shard ID = 16 位,类型 包含数据的 = 10 位,并且该数据在表中的位置 (本地 ID)= 36。

精明的添加学专家会注意到,只有添加 到 62 位。我过去在编译器和芯片设计方面的经验告诉我 储备钻头的重量是黄金。所以我们有两个(设置为 零)。

这是否意味着:

问题一:

它们可以有 2^16,范围为 0-65536 的分片?

类型 ID 为 2^10,范围 0-1024 类型?

Local ID 2^36,范围 0-68719476736 local id's ?

我也在尝试在 Java 中复制他们的哈希函数

|表示将两组 32 位分开,以便于可视化。

#表示位移46位,包含18位,其中2位保留-ShardId

~ 表示位移 36 位,包含 10 位 - 类型 ID 剩下的 36 位 - 本地 ID:

#0000 0000 0000 0000 00#~00 0000 0000~ 0000 | 0000 0000 0000 0000 0000 0000 0000 0000 |


  1. ShardID 3429 的二进制文件 = 1101 0110 0101
  2. 因此 (hashedValue >> 46) = 00 0000 1101 0110 0101 &
  3. 0xFFFF = 1111 1111 1111 1111
  4. ShardId = 00 0000 1101 0110 0101

问题二:

我理解需要 1. 和 2. 但是我不明白为什么我们需要按位运算符 & 0xFFFF,因为 4 和 2 实际上是相同的。

问题三:

我收到以下编译器错误:int 类型的文字 0xFFFFFFFFF 超出范围

public class BitExampleTest {
    public static void main(String[] args) {
      long pinId = 241294492511762325L;
      unHash(pinId);
    }

    private static long hash(int shardId, int typeId, int localId){
        return (shardId << 46) | (typeId << 36) | (localId << 0);
    }

    private static void unHash(long hashedValue){
               long shardID = (hashedValue >> 46) & 0xFFFF; 
               long typeID  = (hashedValue >> 36) & 0x3FF;
               long localID = (hashedValue >>  0) & 0xFFFFFFFFF;

               System.out.printf("shardID %s \n",shardID);
               System.out.printf("typeID %s \n",typeID);
               System.out.printf("localID %s \n",localID);
    }
}

【问题讨论】:

  • Q3:使用文字长后缀:0xFFFFFFFFFL
  • 很有趣的是,SO 没有关于位掩码的深入帖子。来自我的 +1
  • 这里没有“散列值”。所有输入数据都存在于输出中。

标签: java


【解决方案1】:

它们可以有 2^16,范围为 0-65536 的分片?

分片 ID 为 16 位。因此,216 个不同的 Shard ID 是可能的。

类型 ID 为 2^10,范围 0-1024 类型?

类型 ID 为 10 位。因此,210 个不同的类型 ID 是可能的。

本地 ID 为 2^36,范围为 0-68719476736 本地 ID?

本地 ID 为 36 位。因此,236 个不同的本地 ID 是可能的,即指向表中 236 个位置的指针是可能的。

现在,参考Pinterest post,用于演示的Pin ID是241294492511762325。

乍一看,这个值显然不适合 Java int 数据类型。所以,我们切换到long

// 'L' added to tell the compiler it is a long, not an int
System.out.println(Long.toBinaryString(241294492511762325L)); 

// output (padded with 0's on the left)
0000 0011 0101 1001 0100 0000 0001 0000 0000 0000 0110 1011 1111 0111 1001 0101

最初,241294492511762325 的 ID 看起来像这样...

xx 00 0011 0101 1001 01 00 0000 0001 0000 0000 0000 0110 1011 1111 0111 1001 0101
XX [____SHARD(16)_____] [_TYPE(10)_] [________________LOCAL(36)_________________]

要获得 Shard 位,将 ID 右移 (10 + 36) 46 即可。这会得到我们。请注意,XX 左边的位可以是 0 或 1,具体取决于“最后两位”的符号扩展等。

xx 00 0011 0101 1001 01
XX [____SHARD(16)_____]

与 0xffff 进行按位与

           'our 2 golden bits'
                   ▼▼
  xxxx xxxx xxxx xxxx 0000 1101 0110 0101
& 0000 0000 0000 0000 1111 1111 1111 1111
= 0000 0000 0000 0000 0000 1101 0110 0101

无论前导位设置为什么,现在它们都是 0。我认为这应该让你清楚,按位与 0xffff 背后的原因。如果它们用 0 填充,那就太好了。如果他们不是 AND 会处理它。 :)

当你初始化像 0xFFFFFFFFF 这样的字面量时,如果没有后缀并且变量是整数类型(int、long 等),则假定值是 int。而且,int 可以保存 32 位,而不是像您尝试的 36 位(9 x 0xF = 9 x '1111')。因此,您必须使用容量为 64 位的long。将“L”或“l”附加到值的末尾,如 0xFFFFFFFFFL,应该注意编译器错误。 [Reference]

【讨论】:

    【解决方案2】:
    1. 是的,您的范围是正确的,除了最大值是 2n - 1。

    2. 如果忽略,最后 2 位(“价值连城”)可能会对 shardId 值造成严重破坏。该位确保它们被屏蔽掉。如果您决定在未来实现这 2 位,这也会有所帮助。

    3. Java 中的 int 类型限制为 32 位。因为每个十六进制数字代表 4 位,所以在 int 文字中最多可以有 8 个十六进制数字,但您有 9 个 Fs。您需要 long 文字,通过附加 L

    long localID = (hashedValue >>  0) & 0xFFFFFFFFFL;
    

    因为您的localID 是36 位,int 不足以容纳所有可能的值,原因同上。您的hash 方法应将localId 作为long。当所需值为 64 位(long)时,您可能需要将值转换为long,然后再将它们向左移位以避免解释为int

    【讨论】:

      猜你喜欢
      • 2019-07-11
      • 2011-01-17
      • 2011-10-11
      • 2016-11-14
      • 2019-01-15
      • 1970-01-01
      • 1970-01-01
      • 2011-04-09
      • 2015-01-28
      相关资源
      最近更新 更多