contributed by < gawei1206 >
以 Digit-by-digit calculation 方式求得 的平方根
= = , =
= ,而 即為 的平方根
再來可以觀察到:
= +
因為現在的目的是要求出 ,也就是要從 累加到 ,在每回合判斷是否加入
同先前版本實作的效果
由於每輪計算 的成本過高,因此改為利用上輪的差值 減去 可得
也就是紀錄上一輪的 來計算 。
再進一步將 拆成兩個變數相加,
藉由 算出
最後以上述的推導求出 即從 累加到 的和,以下為參數的對應:
z :
m :
b :
x :
其中 if (x >= b)
就是以 和 判斷 ,即
以 ffs
取代 __builtin_clz
:
先前的方法是以 (31 - __builtin_clz(x))
來計算 MSB,ffs()
的回傳是目前 LSB 的位置,以每次求得的 LSB 位置累加並右移,也可以計算出 MSB。
但不知道不依賴 GNU extension 會有什麼優點
提供分支和無分支 (branchless) 的實作:
參考 LindaTing0106
同學的作法,將迴圈中的判別式改寫,達到無分支的實作。
採用 bitwise operation 來實作除法,但是除數無法直接用 2 的冪來表示的話結果會是不精確的,因此我們的目標是計算新的除數,使 tmp / 10
直到小數點後第一位都是精確的。
題目的除數為 10
,再來因為a[i]
, b[i]
的範圍是 0 ~ 9
,carry
則是 0 ~ 1
,,所以 tmp
的範圍是 0 ~ 19
。
為這邊想要求得的新除數,可以用 2 的冪來表示且 仍然會在精確度內,其中 為非負整數 為正整數。
證明猜想成立後,接下來只需要針對 19
來做討論即可。
接下來需要找出可以符合條件的 和 ,題目以 為其中一解,使得 符合條件。
嘗試透過 bitwise operation 來配對數值,首先要得到
接下來再左移 3 bit 後就可以得到 ,但在右移時會有部分位元被捨棄,因此要另行處理。
但在這部分我有些疑惑,因為上述程式碼所計算出來的結果有時可能不是 ,而只是接近
雖然最後要再右移 7 bit 結果應該是不影響,但如果真的要將被捨棄的位元加回去,應該如下較為正確:
再透過 算出餘數。
最後包裝成題目的程式碼如下
首先 x = (in | 1) - (in >> 2)
算出 ,其中 (in | 1)
表示當 in
為偶數時會將它加一,再來 q = (x >> 4) + x
的意思是 ,後續四行重複的操作是為了將結果逼近 ,最後右移 3 bit 得到 。
一樣透過 算出餘數,q & ~0x7
與 *div << 3
是一樣意思的,再加上 *div << 1
求得 ,即可計算出餘數。
實作 ilog2
的目標是找出二進位表示的 msb,在第二種實作方法中的想法是以二元搜尋快速找到 msb,相較於方法一逐位元的判斷來說更有效率。
運用 GNU extension __builtin_clz
來改寫,為了避免 0
的例外狀況需將輸入與 1
做 or。
在對 ewma
結構體初始化時,會確保 weight
及 factor
的值是 2 的冪,目的是為了保證後續位元操作的正確性,代替乘除法的使用。
在後續的程式碼中需要更新 的值,在等號右邊的 internal
代表的是 ,而 val
代表的是 ,由下方的程式碼可以發現,在 internal
是 0 的時候程式會執行 (val << avg->factor)
,所以這邊推測上方的 FFFF 也是做相同的操作,再來觀察
(((avg->internal << EEEE) - avg->internal) + (val << avg->factor)) >> avg->weight
這段程式碼與 的關係,可以轉換成 ,可以發現到 就是 ,再將 乘進去想要算出 ,所以 EEEE 就是
avg->weight
。
與第三題類似,目的是要算出 MSB,但這邊要求出 ,所以當 x
為二的冪時需要先將 x--
,使得最後結果正確。
後續步驟與第三題找出 MSB 的部分相同,不過是以 branchless 的方式實作,以變數 r
紀錄位移量,因為位移量都是二的冪,所以 r |= shift
可以看做 r += shift
,
最後 return (r | shift | x > 1) + 1;
可以看做兩部分,r | shift
是將前一次的 shift
加上,而在最後一次位移後 x
的值可能會是 0x3 或是 0x2,所以後半部會判斷 x
是否大於 1 並將它加上,最後再加上 1 達到取 ceil 的效果。
改進程式碼,使其得以處理 x = 0 的狀況,並仍是 branchless:
我們需要判斷 x
是否大於 0 再將其減一,在之前的作業中有看過 likely 還有 unlikely,其中這兩個巨集的實作中有用到 !!(x)
的用法,目的是透過兩次 NOT 來確保值一定是 0 或 1,所以可將程式中 x--
的部分修改如下: