1. 程式人生 > >雪花演算法(06)再說幾個位運算

雪花演算法(06)再說幾個位運算

n位二進位制表示的最大值

雪花演算法已經初步完成了。現在我們再來看幾個位操作。先看第一個,還是左移操作,不過這裡演示負數左移:

<<

看這個之前,我們先看一個關鍵的數字,最大的負整數,-1L轉換為二進位制後的形式:

這裡注意二進位制數字的思路是相反的,在負整數中,除去負號外,那個數字越大,這個負數就越小,在Java的二進位制形式中,首位代表正負號,除去首位,剩下的數字值越大,真的就代表數字本身越大,無論正負。從上面列印可以看出,-1L的二進位制形式就是一個最大負整數。

我們前面討論位運算提到過左移運算 << ,那麼負數左移會出現什麼情況的呢?下面來看一個例子:

從字面值上來看,負整數左移和正整數左移效果是一樣的,就是把字面值變小了,二進位制的形式也能看出,所有的1左移後,右面直接補0,效果也是把數字變小了。

前面我們說過兩個位移操作,那兩個我們主要是關注二進位制形式的數字效果,這裡我們就要看字面值的變化了。-1L向左位移1位,字面值就變成

-1L * 2^1

如果向左位移n位,字面值就會變成:

-1L * 2^n

這就是-1L向左位移的字面值變化規律。

看完負數左移操作,再來看一個位移操作,取反操作:

~

取反的意思也是針對二進位制形式的數字說的,因為所有位上的數字不是0就是1,所以取反的操作就是把0變成1,把1變成0,來看幾個例子:

上面的正整數3L,取反後,字面值變成了-4L,二進位制的數字中的0和1也徹底反了,0變成1,1變成0。而負整數-9L字面值變成了8L,二進位制數字的變化也是一樣的規律。大家可以多試幾次,從上面的內容可以總結出取反的規律:

1、取反後,正整數變成了負整數,負整數變成了正整數

2、取反後,無論原來是正數還是負數,結果都會變成   (n+1) * -1L

取反操作我們也不看二進位制數字的變化,但看字面值的變化,可以總結出上面的規律。

現在有個需求,如果有三位二進位制數,那麼能表示的最大正數就是 111,也就是7,如果有四位就是1111,也就是15,如果有n位如何用位運算表示?其實公式很容易推出來,就是

2^n-1

這個公式和上面的負數左移很相似,我們來使用-1L進行左移:

-1L << n

這樣n如果是3和4就分別對應-8L和-16L,從字面值上看和我們的需求很接近,我們再來進行取反操作:

~(-1L << n)

這樣3和4分別對應的就是7和15了!上面這個位運算公式,就是求出n位二進位制數能表示的最大整數的公式!

再來看雪花演算法中的限制,資料中心id和機器id分別佔5位,最大數都是31,毫秒內序列佔12位,最大值是4095,這個值直接定義上是最快的,現在也可以用高效的位運算計算出來了:

不超過最大值的序列遞增

雪花演算法的毫秒內序列有兩個操作,一個是在同一毫秒內加一,另一個是如果超過最大值,就強制等到下一個不同的時間從新開始序列。這裡也是可以用位操作實現的。這裡首先討論下面的位操作:

&

這個操作的意思是同一個位上,都是1,結果才是1,否則就是0.下面用二進位制數字演示一下:

15L 轉換為2進位制形式後有個特點,就是前面所有位上都是1,那麼這個時候,其實較小的數是多少結果就是多少。從而可以推理出,只要較大的數有這個特點,那麼&操作的結果都和較小的數的值一樣。如果超過最大值會怎麼樣呢?

可以看到,只要超過1,那麼結果就會歸0,再加上1, 15L & 17L的話,結果又會是1。這樣的&操作可以防止數字超過某個最大限制。這種特性正好用在毫秒內序列的加一操作上。

seq = (seq + 1) & 4095;

像上面那種寫法,其實最終值和

seq = seq + 1

效果是一樣的,不同的是,一旦超過了4095,seq會從新變成0。

程式碼地址:https://gitee.com/blueses/s