# Bitwise Operation ###### Tags: `C`, `Bitwise` ###### References: [`1`](https://puremonkey2010.blogspot.com/2011/05/c-bitwise-operation.html), --- ## Functions ### `SHIFT LEFT <<`, `SHIFT RIGHT >>` >這兩個運算子的功能主要是移動一個變數中的所有位元,位元向左 / 向右移動之後,最高位 / 最低位的位元會消失,最低位 / 最高位的位元補 0 ``` 5 << 1 = 10 <==> 00100 << 1 = 01010 5 << 2 = 20 <==> 00101 << 2 = 10100 5 >> 1 = 2 <==> 00101 >> 1 = 00010 5 >> 2 = 1 <==> 00101 >> 2 = 00001 ``` :::info **當全部位元向左移動一位時,會變成原來的兩倍,向左移動一位時,會變成原來的1/2倍。** ::: --- ### `AND &` >`&` 的功能是將兩個變數對應的位元進行 `AND` 邏輯運算,然後產生新變數。用來判別位元是不是一也很方便。 ``` 0 & 0 = 0 0 & 1 = 0 1 & 0 = 0 1 & 1 = 1 ``` **測試有多少位元的值=1:** ```c #include <stdio.h> int main(){ int num = ***; int digit = sizeof(num) * 8; // 待測數為幾位元 int count = 0; //for(int i=1; i<=num; i=i*2){if(num & i) count++;} for(int i = digit-1; i >= 0; i --) //略去高位0. if(num &(1<<i)) count++; printf("%d\n", count); return 0; } ``` ```c int mark_9th_bit(int n) // 標示第九位元的值 { return n & (1 << 8); } ``` --- ### `OR |` >`|` 的功能是將兩個變數對應的位元進行 `OR` 邏輯運算。 **例如:** ``` 0 | 0 = 0 0 | 1 = 1 1 | 0 = 1 1 | 1 = 1 ``` --- ### `XOR ^` >`^` 的功能是將兩個變數對應的位元進行 `XOR` 邏輯運算。==可以用來反轉某位元的值,也可以用來做簡單加密。== ``` 0 ^ 0 = 0 0 ^ 1 = 1 1 ^ 0 = 1 1 ^ 1 = 0 ``` **反轉某位元** ```c int reverse_9th_bit(int n) // 反轉第九位元的值 { return n ^ (1 << 8); } ``` **不使用暫存變數(temporary variable)交換兩個相異變數** ```c // a should not equal to b a = a ^ b b = a ^ b a = a ^ b ``` **加密** ```c #include <stdio.h> int main(void) { char ch = 'D'; printf("before encoding:%c\n", ch); ch = ch ^ 0xC; printf("after encoding:%c\n", ch); ch = ch ^ 0xC; printf("decoding:%c\n", ch); return 0; } ``` **OUTPUT** ``` before encoding:D after encoding:H decoding:D ``` --- ### `NOT ~` >`~` 的功能是顛倒一個變數每一個位元的 0 和 1。 ``` ~0 = 1 ~1 = 0 ~00101101 = 11010010 ``` --- ## Applications - ### **整數變號** ```c int negative(int x) { return ~x + 1; // -x; } ``` - `NOT` 後 `+1` -> ==二補數== -> 正負變號 - ### **判斷奇偶** ```c bool is_odd(int x) { return x & 1; // x % 2; } ``` - 觀察最低位元是否為 `1` - TRUE,奇數 - FALSE,偶數 - ### **整數取絕對值** ```c int abs(int x) { // x < 0 ? -x : x; // x >> 31 = 111...111 (如果x是負數) or 000...000 (如果x是正數) // x ^ (x>>31) => 如果 x 為負數則將 x 的 0轉1, 1轉0, 如果 x 為正數, 則保持x 不變. // (x ^ (x >> 31)) - (x >> 31) => 如果 x 為正數則 x-0=x , 如果 x 為負數則 ~x-(-1) = -x return (x ^ (x >> 31)) - (x >> 31); } ``` ```c #include <stdio.h> long int labs(long int x) { int n = sizeof(x)*8 - 1; // how many digit - 1 return (x ^ (x >> n)) - (x >> n); } int main(){ long int num = -1556543547665469; printf("%ld\n", labs(num)); return 0; } ``` - ### **最低位元`1`** ```c #include <stdio.h> int lowest_bit_1(int x) { print_bin(x); printf("\n"); print_bin(-x); printf("\n"); return x & -x; } int main(){ int num = 1638; printf("%d\n", lowest_bit_1(num)); } ``` ==[**原理:**](https://stackoverflow.com/questions/12247186/find-the-lowest-set-bit)== :::spoiler ``` number: 0000001101111000 complement: 1111110010000111 ``` Then we add 1 to it. ``` number: 0000001101111000 complement: 1111110010000111 add 1: 1111110010001000 ``` Note that if the rightmost bit is 1, this would create a carry which flips all 1s into 0s until a 0 is reached. This number is now actually also the binary representation of -number. ``` number: 0000001101111000 complement: 1111110010000111 add 1: 1111110010001000 -number: 1111110010001000 ``` We now take the bitwise & of number and -number. ``` number: 0000001101111000 -number: 1111110010001000 number & -number: 0000000000001000 ``` ::: - ### 最高位元`1` ```c //O(log n) unsigned int hibit(unsigned int n) { n |= (n >> 1); n |= (n >> 2); n |= (n >> 4); n |= (n >> 8); n |= (n >> 16); return n - (n >> 1); } int main(){ int a = 145; printf("%d\n", test(a)); } ``` ```c unsigned int hibit(unsigned int x) { unsigned int n; while ( (x & (x - 1)) != 0 ) { x &= (x-1); } return x; } ``` - ### **判斷是不是 `2` 的次方** ```c bool is_power_of_2(int x) { return (x & -x) == x; } ``` ==**原理:**== - 因為如果是二的次方,則從二進制觀察時,只會有一個 set bit (e.g. `0010 0000`)。 所以 `(x & -x)` 得到的最低位 set bit,若又等於 `x` 本身,則表示 `x` 只有一個 set bit,也就是 `x` 為二的次方數。 - ### **交換兩個 `int` 變數** ```c void swap(int& x, int& y) { x = x ^ y; // x' = x ^ y y = x ^ y; // y' = x' ^ y = x ^ y ^ y = x x = x ^ y; // x = x' ^ y' = x ^ y ^ x = y } ``` - ### **計算有幾個位元是`1`** ```c int count_bits2(unsigned int n) { int i=0; for (; n != 0; n>>=1) if (n & 1) ++i; return i; } ``` ==**原理:**== - 一直將變數 `right shift 1` 並 `AND 1`,若為 `TRUE` ,則 `++count`。 -- ```c unsigned char Cal_bit1_Num(int sample) { unsigned char Num = 0; while (sample) { Num++; sample = (sample - 1) & sample; } return Num; } ``` - ### **顛倒位元順序** - Common Solution: ```c int reverse_bits(int x) { x = ((x >> 1) & 0x55555555) | ((x << 1) & 0xaaaaaaaa); x = ((x >> 2) & 0x33333333) | ((x << 2) & 0xcccccccc); x = ((x >> 4) & 0x0f0f0f0f) | ((x << 4) & 0xf0f0f0f0); x = ((x >> 8) & 0x00ff00ff) | ((x << 8) & 0xff00ff00); x = ((x >> 16) & 0x0000ffff) | ((x << 16) & 0xffff0000); return x; } ``` ==**解釋**== :::spoiler ``` x = 0101 1011 0001 0x55555555 -> 0101 0101 0101 0101 0101 0101 0101 0101 0xaaaaaaaa -> 1010 1010 1010 1010 1010 1010 1010 1010 (x >> 1) & 0x55555555) | ((x << 1) & 0xaaaaaaaa) = 0010 1101 1000 & 0101 0101 0101 | 1011 0110 0010 & 1010 1010 1010 = 0000 0101 0000 | 1010 0010 0010 = 1010 0111 0010 0x33333333 -> 0011 0011 0011 0011 0011 0011 0011 0011 0xcccccccc -> 1100 1100 1100 1100 1100 1100 1100 1100 0x0f0f0f0f -> 0000 1111 0000 1111 0000 1111 0000 1111 0xf0f0f0f0 -> 1111 0000 1111 0000 1111 0000 1111 0000 0x00ff00ff -> 0000 0000 1111 1111 0000 0000 1111 1111 0xff00ff00 -> 1111 1111 0000 0000 1111 1111 0000 0000 0x0000ffff -> 0000 0000 0000 0000 1111 1111 1111 1111 0xffff0000 -> 1111 1111 1111 1111 0000 0000 0000 0000 ``` ::: - Use `uint32_t`: 關於 `uint32_t` 定義可以查看以下 :::spoiler **<stdint.h>** ```c /* Exact integral types. */ /* Signed. */ /* There is some amount of overlap with <sys/types.h> as known by inet code */ #ifndef __int8_t_defined # define __int8_t_defined typedef signed char int8_t; typedef short int int16_t; typedef int int32_t; # if __WORDSIZE == 64 typedef long int int64_t; # else __extension__ typedef long long int int64_t; # endif #endif /* Unsigned. */ typedef unsigned char uint8_t; typedef unsigned short int uint16_t; #ifndef __uint32_t_defined typedef unsigned int uint32_t; # define __uint32_t_defined #endif #if __WORDSIZE == 64 typedef unsigned long int uint64_t; #else __extension__ typedef unsigned long long int uint64_t; #endif /* Small types. */ /* Signed. */ typedef signed char int_least8_t; typedef short int int_least16_t; typedef int int_least32_t; #if __WORDSIZE == 64 typedef long int int_least64_t; #else __extension__ typedef long long int int_least64_t; #endif /* Unsigned. */ typedef unsigned char uint_least8_t; typedef unsigned short int uint_least16_t; typedef unsigned int uint_least32_t; #if __WORDSIZE == 64 typedef unsigned long int uint_least64_t; #else __extension__ typedef unsigned long long int uint_least64_t; #endif /* Fast types. */ /* Signed. */ typedef signed char int_fast8_t; #if __WORDSIZE == 64 typedef long int int_fast16_t; typedef long int int_fast32_t; typedef long int int_fast64_t; #else typedef int int_fast16_t; typedef int int_fast32_t; __extension__ typedef long long int int_fast64_t; #endif /* Unsigned. */ typedef unsigned char uint_fast8_t; #if __WORDSIZE == 64 typedef unsigned long int uint_fast16_t; typedef unsigned long int uint_fast32_t; typedef unsigned long int uint_fast64_t; #else typedef unsigned int uint_fast16_t; typedef unsigned int uint_fast32_t; __extension__ typedef unsigned long long int uint_fast64_t; #endif /* Types for `void *' pointers. */ #if __WORDSIZE == 64 # ifndef __intptr_t_defined typedef long int intptr_t; # define __intptr_t_defined # endif typedef unsigned long int uintptr_t; #else # ifndef __intptr_t_defined typedef int intptr_t; # define __intptr_t_defined # endif typedef unsigned int uintptr_t; #endif /* Largest integral types. */ #if __WORDSIZE == 64 typedef long int intmax_t; typedef unsigned long int uintmax_t; #else __extension__ typedef long long int intmax_t; __extension__ typedef unsigned long long int uintmax_t; #endif . . . ``` https://sites.uclouvain.be/SystInfo/usr/include/stdint.h.html ::: ```c uint32_t reverseBits(uint32_t n){ uint32_t m = 0; for(int i=0; i<32; i++, n>>=1){ m <<= 1; m |= n&1; } return m; } ``` - ### 改某個`bit`為`1`或`0` ```c void bit_ctrl_0 (char* pflag, int bit) { *pflag &= ~(1 << bit); //改0 -> 先用mask,再用and } void bit_ctrl_1 (char* pflag, int bit) { *pflag |= (1 << bit); //改1 -> 直接or } ```