contributed by < ptzling310
>
1
: 有號整數的跨平台 ASR 實作依據 ISO/IEC 9899:TC2 (即 C99) 標準的 6.5.7 章節 (第 84 到第 85 頁):
“The result of
E1 >> E2
is E1 right-shifted E2 bit positions. … If E1 has a signed type and a negative value, the resulting value is implementation-defined.”
>> 1
m < 0
首先這邊先判目前所在的平台是採算數右移或是邏輯右移,藉由 -1
來測試。 所以 OP1= >>1
。
接著判斷輸入的 m
是否需要進行 ASR 的修正, 如果 m > 0
,表示 m
是正數,不論平台做邏輯或算數右移,其結果都是對的。
若m < 0
,表示 m
是負數,若 logical == 1
,就要對該數右移的結果進行修正。所以OP2 = ( m < 0 )
。
故 logical & (m < 0)
之結果就可能為 (0000 0000)2 或者 (0000 0001)2,為了要讓最後產生 1 在 MSB 的位置, 所以在 logical & (m < 0)
前面加上 -
。 最後 fixu
會有兩種可能,分別為 (0000 0000)2 = 0 或者 (1111 1111)2 = (-1)。
就結果來看, fix
與 fixu
所存的值應該是一樣的,那為何要不要直接寫 int fix = = -(logical & (m < 0));
?
參照 jserv
在 RinHizakura
中的回覆:避免編譯器進行過度 (aggressive) 最佳化
TODO:過度最佳化會帶來的影響
在 return 這階段來做右移修正, 當m >> n
後, m 的二進制的最左 n 個 bit 會為 0 , 若 m 為負數時,這些最左 n 個 bits 應為 1 ,故藉由(fix ^ (fix >> n))
來產生所需要的 n 個 1 補在 m >> n
的最左 n 個 bits。
Question: 會有哪一類型的圖產生?
可參照 Swift: Advanced Operators 精美的圖示,用來說明算術位移,你可搭配數學證明解釋通用 (generic) 的狀況
jservImage Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
所以右移後的結果 >0
,故 logical = 1
因為 logical = 1
且 m = -5 < 0
,logical & m < 0
為 1
。
故 fixu =
(1…1)2。
最後 fixu
是上圖結果再取 -
。
其實就是 fixu
的值,但 fix
為 signed。
假設 m = -5 , n = 1
Question:其他寬度的意思?
指 int16, int32, int64, 等等。width 就是「資料寬度」,不要因為讀了一堆對岸文章,就忘了繁體中文怎麼書寫
jservImage Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
#include
#define
#define
: #define 巨集名稱 算式
#define
若為多行,需在每一行最後加上\
#include
以及 main()
中間\
後不可有空格,否則會出現 warning: backslash and newline separated by space
改寫
結果
2
:LeetCode 342. Power of Four& 0x1
分別來看 3 個判斷條件:
num > 0
:因為 > 0(num & (num - 1))==0
:因為 ,所以若 num
是 4 的冪,則必為 2 的冪,其二進位必定長得像 (1xx…x)2,(num - 1)
長得會像 (011…1)2, 兩者在做&
則必為 0。!(__builtin_ctz(num) OPQ)
:__builtin_ctz(num)
是回傳 num
的二進制的尾巴有幾個 0
。觀察下面幾個:
二進制 | |
---|---|
(100)2 | |
(10000)2 | |
(1000000)2 | |
(100000000)2 |
觀察到尾數 0
的個數的變化是 2 → 4 → 6 → 8 … ,也就是說 0 的尾數應該要是偶數個。
故 (__builtin_ctz(num) OPQ)
判斷 __builtin_ctz(num)
的 0 的個數為奇數個。而奇數的特徵就是,其 b0(LSB) 為 1
,利用 &
(0..01
)2 來讀取 b0看是否為 1 。 得OPQ = & 0x1
。
__builtin_popcount
來確認。而且也可以排除0
的情況,就可以不用做num > 0
。0x55555555
= (0101 0101 ... 0101
)2 與 num
做 AND
來確認 bit 1 是否位在 b偶數。1...1
)2 做XOR
即可得 0 、 1 互換。input = 5 = (0101)2
input ^ (1111)2 = (1010)2 = -6
expect output = 2 = (010)2
所以應該是要找到最左的 bit 1 後才做XOR
!
__builtin_clz
),再針對這些 0 以後的 bits 做XOR
。i
個元素不等於 i+1
, i+1
就是要找的。i
個元素皆等於 i+1
,則回傳 numsSize+1
。TODO
3
: LeetCode 1342. Number of Steps to Reduce a Number to Zero計算出將 number 藉由 /2
以及 -1
計算至 0 的步驟數。
假設 int
寬度為 32 位元
__builtin_popcount(num)
__builtin_clz(num)
假設 num
為 32 bits ,假設每個 bit 都會做右移(/2
),則至少做 32-1 次 (因為最後一個留下的 bit 並不用在右移,頂多只要 -1),但如果在 num
中 bn = 1 時,必須多花一個步驟做 -1
,所以 AAA 應為 __builtin_popcount(num)
。
如: (00010010)2),最左只會做到最左邊的那個 1 就會停止,則不用再做右移,我們就將不用右移的次數扣除,所以 BBB 為 __builtin_clz(num)
。
4
: 64-bit GCD (greatest common divisor, 最大公因數)v
u << shift
for
的終止條件是 !((u | v) & 1
,表 u
或 v
之 b0 不為 1,才進入for
迴圈,簡單來說就是否兩者皆為偶數。這邊是進行 。利用 shift
來記錄有幾個 2 要乘在 前 (可利用左移來完成!)。所以接下還的 頂多 或 是偶數。
如果 是偶數,則一樣/2
,進行。
Line 11 如果 是偶數,則/2
,進行。
所以 line 11 後, 內的數都是奇數了!
再來又分兩種情況,一個是 u < v
,此時我們就不斷將 v - u
;另一個是 u > v
,我們利用變數 t
的幫助,轉換為 u < v
的 case 去做。而 whiel
的結束點就是 v
,所以 XXX = v
。而 u
會記錄在一連串輾轉相處法的過程中所得出的最大公因數。
但在 line 5 ~ 7 中,我們有利用 shift
來記錄我們要補回幾次 2 ,這樣才會是正確結果。故 YYY = u << shift
。
5
: 找 bit array (aka bit map, bit set, bit string, or bit vector) 中 bn=1 的位址參數:
uint64_t
*bitmap : bitmap point to a bit array.size_t bitmapsize
: 有 bitmapsize 個 bitmap ,每一個 bitmap 有 64 bits 。uint32_t *out
: 要將結果儲存在 out 中從這段可以看出來,我們每次都是去比較 1 個 bit (每次把 bitset 往右移動,在取 b0 看是否為 1 ),當遇到 1 時,我們就把這個位子記錄到 out 中。
不過這樣的處方法,可以再改進,最右的 bit 0 個數如果很多,就乾脆都不要看,最快的方法是我們直接找到最右邊的 bit 1 ,且紀錄位子。 此作法及可透過 __builtin_ctzll
來達成。
KKK = bitset & -bitset
程式理解
藉由 k
來記錄我們應該確認的 bitmap (共 bitmapsize 個)。
bitset
就是一次取一個 bitmap 來看。
__builtin_ctzll
: Returns the number of trailing 0-bits in x, starting at the least significant bit position. If x is 0, the result is undefined.
所以透過 builti_ctzll
我可以推算出最右的 bit 1 在哪個位子。
為了找到第二個 bit 1 ,那會希望可以將第一個 bit 1 先忽略掉,所以這裡會希望能夠將目前最右的 bit 1 設置為 0
,而其他不變。 故 t 希望為 (0...0 1 0..0
)2 而 1 的位子正好是與目前最右 bit 1 的位子相同。
所以 KKK = bitset & -bitset
, 因為 -bitset
的結果是由右至左,遇到第一個 bit 1 保留,其餘的 0 ↔ 1 。
1
、2
、3
程式理解。4
程式理解、浮點數運算5
程式理解、測驗 1
延伸問題_21
延伸問題_3、C 語言:前置處理器應用篇2
延伸問題_1、2sysprog2020