contributed by <RusselCK
>
RusselCK
在 C 語言中,對有號型態的物件進行算術右移 (arithmetic right shift,以下簡稱 ASR 或 asr) 歸屬於 unspecified behavior
提示: 透過 gcc/clang 編譯程式碼時,可加上 -fsanitize=undefined
編譯參數,在執行時期若遇到未定義行為,會得到以下錯誤訊息:
runtime error: load of misaligned address 0x7ffd8a89be8f for type ‘int’, which requires 4 byte alignment
以 8 bits 為例:
-5 >> 1 = ( 1111 1011 ) >>1 = ( 1111 1101 ) = -3
(fix ^ (fix >> n))
可為 0 或 正確的 mask(fix ^ (fix >> n))
必須為正確的 mask ,將補位換成我們想要的值
正確的 mask : ( n = 1 )
fix = ( 1111 1111 )
(fix ^ (fix >> n))
= ( ( 1111 1111 ) ^ ( 0111 1111 ) ) = ( 1000 0000 )
-1 = (1111 1111)
#3
來得知((int) -1) >>1)
為 ( 0111 1111 ) ,代表電腦在負整數 ARS 會補 0,logical
為 true
= ( 0000 0001 )((int) -1) >>1)
為 ( 1111 1111 ) ,代表電腦在負整數 ARS 會補 1,logical
為 false
= ( 0000 0000 )( p. 85 )
The result of E1 >> E2 is E1 right-shifted E2 bit positions.
If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / . ( i.e. 正整數 ARS 不會出錯 )
If E1 has a signed type and a negative value, the resulting value is implementation-defined.
logical
= true
( i.e. 電腦在負數 ARS 補位的方式不正確 )
fixu
為 ( 1111 1111 )-(logical & (OP2))
= ( 1111 1111 )(0000 0001) & OP2
= ( 0000 0001 ) // 2's complement
true
logical
= false
fixu
為 ( 0000 0000 )-(logical & (OP2))
= ( 0000 0000 )(0000 0001) & OP2
= ( 0000 0000 ) // 2's complement and overflow
false
m < 0
logical
= false
,OP2 = m < 0
仍成立問題:( Form RinHizakura 同學 )
和
兩個寫法的差異性?
前者可避免編譯器進行過度 (aggressive) 最佳化
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
#1
~#7
: 根據不同的 data model,asr_i(m, n)
會使用相對應的 function#15
~#18
中的asr(type)
會轉換成相對應的內容 ( i.e. #10
~#13
)問題:( Form RinHizakura 同學 )
乍想之下,無論型別都直接轉型成 64 bits 的版本處理,計算出結果後再轉回原本的型別,似乎正確性上是沒有問題的?這樣想是否完全正確呢?排除正確性的話,這樣方式的實作可能有甚麼缺點?
不是每款 C 語言編譯器都能正確處理 64 位元,且 data model 會涉及 ABI 議題
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
__builtin_ctz
實作)int
為 32 位元寬度GCC extension 其中兩個是 ctz 和 clz:
Built-in Function: int builtin_ctz (unsigned int x)
Built-in Function: int builtin_clz (unsigned int x)
in binary | __builtin_ctz() | |
---|---|---|
0000 0001 | 0 | |
0000 0100 | 2 | |
0001 0000 | 4 | |
0100 0000 | 6 | |
… | 2 |
以 為例:
(num & (num - 1)) = (( 0001 0000 ) & ( 0000 1111 ) ) = ( 0000 0000 )
觀察,我們可以發現,只要二進制形式符合下列兩個條件,該數便是 4 的冪
效能分析:
其他的改寫:
int builtin_ffs (unsigned int x)
Input: 5
Output: 2
Explanation: 5 is "101" in binary, with complement "010" in binary, which is 2 in base-10.
以 8 bits 為例
N
= 5 = ( 0000 0101 )2
依據題目要求,應該會需要 ~N
= ~5 = ( 1111 1010 )2 // 1s' complement
__builtin_clz(N)
= 5因此,我們還需要一個 mask
= ( 0000 0111 )2
= ( 0000 1000 ) - 1 = ( 1 << 3 ) - 1 = ( 1 << ( 8 - m ) ) - 1
綜合上述,並推廣到 32 bits,可得:
mask = (1 << ( 32 - __builtin_clz(N) )) - 1
~N & mask
效能分析:
note
的各 bit 紀錄,有哪些正整數出現過
nums
中有 4,則 note |= (0000 1000)
!((note >> j) & 0x1)
找出最早出現 0 的位置,即為所求runtime error: shift exponent 53 is too large for 32-bit type 'int'
Input: [1,2,0]
Output: 3
Input: [3,4,-1,1]
Output: 2
Input: [7,8,9,11,12]
Output: 1
out
有 記錄 bit 為 1 所在位置 的功能
num
也擁有 記錄 的功能#6
~#14
)
num[i]
< numsSize
,則將 num[i]
和 num[num[i]-1]
互換
(pos != i && nums[i] != nums[pos])
)#11
: 交換完之後,num[i]
還要在檢查一次 ( 因為內容有更動 )#16
~#19
)
nums[i] != i + 1
),代表應該出現的內容為 Missing Positive
num[0] == 1
、 num[1] == 2
etc.__builtin_popcount
來實作 LeetCode 1342. Number of Steps to Reduce a Number to Zeroint
為 32 位元寬度population count 簡稱 popcount 或叫 sideways sum,
GCC 提供對應的內建函式: __builtin_popcount(x)
以 14 為例:
- 14 = ( 0000 1110 ) is even; divide by 2 and obtain 7.
- 7 = ( 0000 0111 ) is odd; subtract 1 and obtain 6.
- 6 = ( 0000 0110 ) is even; divide by 2 and obtain 3.
- 3 = ( 0000 0011 ) is odd; subtract 1 and obtain 2.
- 2 = ( 0000 0010 ) is even; divide by 2 and obtain 1.
- 1 = ( 0000 0001 ) is odd; subtract 1 and obtain 0.
__builtin_clz(num)
) - 1__builtin_popcount(num)
__builtin_clz(num)
) - 1 ] + __builtin_popcount(num)
__builtin_popcount(num)
+ 31 - __builtin_clz(num)
builtin_popcount(num)
、BBB = builtin_clz(num)
提示: Bit Twiddling Hacks 中提及許多 bitwise operation 的使用,如 bit inverse、abs 等等,是極好的參考材料。
%
的版本%
的版本shift
紀錄有幾個 因子#12
~#22
便是不斷操作 的部份,直到 v
= 0 為止v
#12
~#22
結束後,v
= 0u << shift
#6
~#9
得到 shift
個 2 因子__builtin_ctz
改寫 GCD#10
~#12
)uint64_t *bitmap
、bitmap[k]
,可以判斷 bitmap 為 uint64_t Arrayuint32_t *out
、out[pos++]
,可以判斷 out 為 uint32_t Array( p. 61 )
wchar_t
, an integer type defined in the <stddef.h>
header.mbtowc
function, with an implementation-defined current locale.#09
: 逐一檢查 bitset
的 64 個 bits ,只要發現該 bit 為 1 (i.e. ((bitset >> i) & 0x1)
為 true ),便進入 #10
#10
: 將 p + i
寫入 out[pos++]
#07
: bitset
的第 1 個 bit = bitmap
中的第 p
個 bitp + i
個 bit 中 ( 以 bitmap
的角度來看 )p + i
) 紀錄於 陣列out
pos
代表 bitmap
中,出現 1 的個數
array 由 0 起算;人類 由 1 起算
因此,需要pos++
__builtin_ctzll
改進版 :builtin_ctzll (unsigned long long)
bitset
之中沒有 1 ( i.e. 全為 0 ),則不用進行檢查,可直接進入下一組#10
、#11
可將 bitmap
中出現 1 的 bit 所在 index 紀錄於陣列 out
中#12
可將 bitset
中 最低位的 1 換成 0,如此一來,下個 while 迴圈才能正確執行
以 8 bits 為例
bitset
= ( **** *100 )t
應為 ( 0000 0100 )-bitset
= ( 100 ) // 2's complement
t
= KKK = bitset & -bitset
應考慮到不同的 bitmap density