Bitwise Operation

Bitwise operator in C/C++

歡迎來到二進位的世界。電腦資料都是以二進位儲存,想當然程式語言的變數也都是以二進位儲存。在 C/C++ 當中有幾個位元運算子: << SHIFT LEFT 、 >> SHIFT RIGHT 、 & AND 、 | OR 、 ^ XOR 、 ~ NOT ,可以對變數進行位元運算。接下來要介紹位元運算的一些用途。

<< SHIFT LEFT
>> SHIFT RIGHT

這兩個運算子的功能主要是移動一個變數中的所有位元,位元向左 / 向右移動之後,最高位 / 最低位的位元會消失,最低位 / 最高位的位元補 0 :

5 << 1 = 10 // 00101 的全部位元向左移動一位數變成 01010。
5 << 2 = 20 // 00101 的全部位元向左移動兩位數變成 10100。
5 >> 1 = 2  // 00101 的全部位元向右移動一位數變成 00010。
5 >> 2 = 1  // 00101 的全部位元向右移動一位數變成 00001。

在十進位當中,當全部位數向左移動一位時,數值大小會變成原來的十倍,向左移動兩位時,會變成原來的百倍。這種情形在二進位也是成立的,當全部位元向左移動一位時,會變成原來的兩倍,向左移動兩位時,會變成原來的四倍。至於往右移動也是類似道理,變成了除法而已。

由於電腦進行位元運算比乘法、除法運算快上許多,所以有很多專業的程式設計師,會利用位元運算來取代乘法、除法運算。優點是程式執行效率增加,缺點是程式碼可讀性變低:

int n = 5;
n = n >> 1; // 即是 n = n / 2 之意。
/* 該式子也可寫成 n >>= 1 或 n /= 2。 */

& AND

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1
& 的功能是將兩個變數對應的位元進行 AND 邏輯運算,然後產生新變數。

   00000000000000000000000001110100 -> 116
 & 00000000000000000000000000101001 -> 41
 ----------------------------------
   00000000000000000000000000100000 -> 32

& 的特色,就是可以判斷出位元是不是 1 。例如我們想要數一個變數有幾個位元是 1 :

int count_bit_1(int n)
{
    // 由右往左看n的每一個位元。
    int c = 0;
    for (int i=0; i<32; ++i)    // int變數有32個位元
        if (n & (1 << i))
            c++;
    return c;
}
 
int count_bit_1(int n)
{
    // 一樣是由右往左,但是每次都刪掉n的最左邊位元。
    int c = 0;
    for (; n; n>>=1)
        if (n & 1)
            c++;
    return c;
}

| OR

0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
| 的功能是將兩個變數對應的位元進行 OR 邏輯運算,然後產生新變數。

   00000000000000000000000001110100 -> 116
 | 00000000000000000000000000101001 -> 41
 ----------------------------------
   00000000000000000000000001111101 -> 125
| 的特色,就是把位元強制標記成 1 。例如我們想要把五位數標成 1 :
int mark_5th_bit(int n)
{
    return n | (1 << 4);
}

^ XOR

0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
^ 的功能是將兩個變數對應的位元進行 XOR 邏輯運算,然後產生新變數。

   00000000000000000000000001110100 -> 116
 ^ 00000000000000000000000000101001 -> 41
 ----------------------------------
   00000000000000000000000001011101 -> 93
^ 的特色,就是把位元的 0 和 1 顛倒。例如我們想要顛倒第五位數:
int reverse_5th_bit(int n)
{
    return n ^ (1 << 4);
}

~ NOT

~ 0 = 1
~ 1 = 0
~ 的功能是顛倒一個變數每一個位元的 0 和 1 。

 ~ 00000000000000000000000000000011 -> 3
 ----------------------------------
   11111111111111111111111111111100 -> -4

Bitwise Trick
交換兩個 int 變數

void swap(int& x, int& y)
{
    x = x ^ y;
    y = x ^ y;
    x = x ^ y;
}
 
void swap(int& x, int& y)
{
    x ^= y ^= x ^= y;
}

判斷一個整數是不是 2 的次方

bool ispow2(int n)
{
    return (n & -n) == n;
}

整數加一與減一

// 注意:比直接加一和減一還要慢。
void add_one(int& x)
{
    return -~x; // ++x
}
 
void sub_one(int& x)
{
    return ~-x; // --x
}

整數變號

void negative(int& x)
{
    return ~x + 1;          // -x;
}
 
void negative(int& x)
{
    return (x ^ -1) + 1;    // -x;
}

判斷一整數是偶數還是奇數

// 若回傳true則為偶數,false則為奇數
bool even_or_odd(int& x)
{
    return x & 1;   // x % 2;
}

非負整數取模數,模數是二的冪次方。

int mod(int& x, int& m) // m是二的冪次方
{
    return x & (m - 1); // x % m;
}

整數取絕對值

int abs(int& x)
{
    // x < 0 ? -x : x;
    return (x ^ (x >> 31)) - (x >> 31);
}

平方根倒數

3D 繪圖經常用到的一個運算,原理是牛頓法。

http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf

Fast Inverse Square Root
// 1.0 / sqrt(x)
float InvSqrt(float x)
{
    float xhalf = 0.5f*x;
    int i = *(int*)&x;
    i = 0x5f3759df - (i>>1);
    x = *(float*)&i;
    x = x*(1.5f-xhalf*x*x);
    return x;
}

計算 32 位元整數有幾個位元是 1

void count_bits(int& x)
{
    x = (x & 0x55555555) + ((x & 0xaaaaaaaa) >> 1);
    x = (x & 0x33333333) + ((x & 0xcccccccc) >> 2);
    x = (x & 0x0f0f0f0f) + ((x & 0xf0f0f0f0) >> 4);
    x = (x & 0x00ff00ff) + ((x & 0xff00ff00) >> 8);
    x = (x & 0x0000ffff) + ((x & 0xffff0000) >> 16);
}

顛倒 32 位元整數的位元順序

void 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);
}

32 位元整數的最高位位元位置

原理是 Binary Search 。

void highest_bit_position(int x)
{
    x--;
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    x++;
    return x;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355

推荐阅读更多精彩内容

  • 为何叫做 shell ? shell prompt(PS1) 与 Carriage Return(CR) 的关系?...
    Zero___阅读 3,153评论 3 49
  • ——Min而好学 风吹麦浪,芒种杏黄。 儿时的记忆,老家西边地里成片成片的麦子...
    lijunhua66878阅读 663评论 2 5
  • 又是一天晨读时!吃早饭的时候侯梦馨说妈妈你别忘了写亲子日记啊!看来她挺中意我写这个呢,还特意嘱咐一遍! ...
    我的大馨妹阅读 187评论 0 0
  • [root@all softs]# yum install etcd -y [root@all softs]# m...
    amoyzhu阅读 4,060评论 0 1
  • 感觉叶子的间距不是很平衡,花也不是太对称的,画完比例有点失调怎么办呢?
    秤坨阅读 237评论 0 0