contributed by <stevendd543>
目標 : 以二進位操作完成 square root 的逼近值
透過 log2(N)<<1
先取得最高有效位,目的是為了不超過 N 下,由大往小找到估計解,可以思考一下為什麼不是從小往大逼近 ? 其實很好理解當你由小往大去疊加你的 result
很容易會超過 N,所以比如說要找 一定是先找到 再找 ,不過這是 10 進位的逼近,二進位一定會有較大誤差,因為依照這個方法其實不會先找到 710 = 01112 而是會先找到 410 = 1002,所以最終估計出來的只能到 (1112)2 = 4910。
其實原本的 log2 只是要用來取得 msb 所以可以不必使用 log2 也能完成。
這段程式碼本身在作字串數字相加,但目標為了提高效率將其除法與乘法替換成 bitwise operation,當然精度肯定會減少,但我們只求小數點後第一位是精準即可,因此才有機會使用 bitwise operation 操作。
首先需要思考的事情是除以10並不能用 2 的冪表示,換句話說不能以右移來代替除法,因此需要數學證明除數,除了 10 外在什麼樣的範圍內可接受且滿足精度在小數點後一位是精準的。
我先講結論,最後可以透過先將 tmp 乘以一個倍數 a ,再除以一個 2 的冪 來代替除法,以數學公式表示 ,那這裡會好奇原本的除以 10 不能依照這個先乘以倍數在除以 2 的冪嗎 ? 答案是不行因為 2N 本身不具有 5 的因數所以不管怎麼除都不會是 。
所以這時需要另外找 10 以外的 來證明什麼時候能滿足精度的要求。
證明可參考講義,推導結果 (1) ,因此只要找到滿足條件 (1) 的 2 的冪和 a。因此當 可以滿足條件。
回到剛剛的除法替代 到這裡還能明白但不知為何需要先將 除以 8 在乘回來(待釐清),目前是認為因為商的結果都會處在較高位,如果一開始就用左移操作,且原本儲存在 bits 就不是很大就有機會吃掉商的值,為了盡可能避免這狀況發生,所以先除後乘法返還。
目標 : 實作 log2x 的二進位求值
版本一 : 透過右移算子向下取整求出整數對數
假設 , ilog2
將會向下取整輸出 。
版本二 : 透過二分搜尋法減少計算量
版本三 : 換句話說其實只要找到最高有效位的 bit 就是答案了,因此可以使用31 - __builtin_clz()
來完成。
Exponential Weight Moving Average
原理:
It maintains a structure housing the EWMA parameters alongside a scaled internal representation of the average value, aimed at minimizing rounding errors.
可以看到程式碼中提到,使用了 structure 儲存 EWMA 參數,其中參數包含了 factor、weight、internal,(1) 這裡的 factor 強調是為了 minimizing rounding errors
也就是定點數的 scaling factor ,(2) 而 weight 則是以 2 的冪來表示,但要注意這裡的 weight 並不是 , 而是透過 log 取得 ,這是為了 bitwise operation 而儲存的形式,因此就可以透過右移 weight 個 bits 來達到乘以 2-n 操作。(3) 最後 internal 全名為 internal representation 簡單翻譯就是內部表達式,換句話說因為這是定點數操作,所以當你乘上 factor 轉換成定點數後,這些平均結果都會以內部表示式來呈現,也就是定點數的呈現方式,當你要透過 unsigned long ewma_read(const struct ewma *avg)
讀取真正的值才會利用 左移 factor 個 bits 來返還真實值。
特別注意到 ewma_add
這個計算 EWMA 的函式,因為我們知道 EWMA 公式為,先將其乘以權重 avg->weight
假設為,, 最後再把結果 >>avg->weight
回來即可。
這段程式碼跟測驗三同樣使用的是二分搜尋法,只是以不同方式表達,
while (i >= 65536)
對應到 r = (x > 0xFFFF) << 4
這裡不用 while 的原因是輸入只有 32bits,不會重複執行。result += 16
對應到 r |= shift
,然後 i >>= 16
對應到 x >>= shift
。
如果 x=0 ,在第一步就會發生向下溢位,出來的結果就會不正確,因此要確保再 x 等於 0 的時候不作減法x += !!x
,不過這個在沒有明確定義輸入錯誤的處理,只能避免溢位,答案仍然會是錯誤的。如果可接受溢位,也可在 x--
後添加 x += (x > (x+1))
。
額外提及一點還有 x 型態為 uint32_t
,因此 x > 0xFFFF
並不會發生,因此此條件永遠不會成立,不過如果考量到通用性,確實可以將其程式碼保留。
計算每對,但因為每一對都會出現兩次因此最後要除以 2。
n = popcount(n & 0x55555555) - popcount(n & 0xAAAAAAAA)