你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

位操作运算——以C语言的交互环境来做代码演示

[复制链接]
gaosmile 发布时间:2020-6-11 20:35
位运算

百度百科如下:

程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作

位操作的优势
  • 位运算是一种底层的运算,往往比我们普通的运算要快上许多许多
  • 位运算是最高效而且占用内存最少的算法操作,执行效率非常高
  • 位运算操作的是二进制数,会拥有一些二进制的特性,在实际问题可以方便运用
  • 位运算只需较低的空间需求
  • 位运算使用能使程序变得更加简洁和优美
  • 位运算可以表示一些状态集合7 m% `  I- t7 E  @1 K! g
运算符号

下面的a和b都是整数类型,则:

含义
C语言
按位与
a & b
按位或
a | b
按位异或
a ^ b
按位取反
~a
左移
a << b
带符号右移
a >> b
无符号右移
优先级

C语言中位运算符之间,按优先级顺序排列为

优先级
符号
1
~
2
<<、>>
3
&
4
^
5
|
6
&=、^=、|=、<<=、>>=
概念简介以及技巧

本文会以C语言的交互环境来做代码演示

常见的二进制位的变换操作

微信图片_20200611203827.png
; M% _/ i, G8 b* E

and运算 &
  • 判断奇偶数) `' `& c% D! V2 N$ e4 C/ \

对于除0以外的任意数x,使用x&1==1作为逻辑判断即可

if (x&1==1)3 ~# D9 t% V) k. |& B2 d
{
( N$ O$ I4 k3 }9 L' ~& W, j! L   
' ~3 n$ y1 X' @0 W8 O  i5 @}
  j1 o! b4 ^# z. Q* T
  • 判断某个二进制位是否为12 J4 S. H8 U$ q8 c' Q& g2 p

比如第7位, 0x40转到二进制是0100 0000,代表第7位是1.

if (n&0x40)$ t1 S- L9 |) ]2 v, _: N
{
, n! e. |! L) @( e# b0 u: t    //TODO:添加你要处理的代码' G$ v2 |- Q7 A+ B. Q
}
* k# J- ^9 p9 C1 d
  • 字节读取
    + W7 N, j0 v: X7 n8 m9 j
(x >>  0) & 0x000000ff        /* 获取第0个字节 */
( f4 a5 L. ?) p(x >>  8) & 0x000000ff        /* 获取第1个字节 */1 _* ?& |  Q, H4 l. L: X- \5 a
(x >> 16) & 0x000000ff        /* 获取第2个字节 */8 x- O) M4 o8 N" ?, A
(x >> 24) & 0x000000ff        /* 获取第3个字节 */

3 F+ s8 K& v0 Y# s+ _
  • 判断一个数是不是 22 的指数4 |: P7 T1 `- q7 G) I  `
bool isPowerOfTwo(int n) {
7 m7 I! W. l% F. O1 f    if (n <= 0) return false;& e1 Q7 I4 Y' s% s! ]% I8 V
    return (n & (n - 1)) == 0;( U: @& `2 E4 W+ l+ U6 l0 [
}
; Z0 K& V1 U" O# ?" ^$ ?
  • 取余
      {- l, f+ K9 F0 b8 t8 ?2 N
//得到余数9 C* \5 F1 [2 g
int Yu(int num,int n)
* p: i7 A. ^: `9 @6 [0 s{
7 E. h9 t) b' K/ Z/ p5 E& ?        int i = 1 << n;
% w  t5 v: p5 y2 i; M9 T        return num&(i-1);5 T8 J! l9 H& x6 `
}
, k; U2 H; R1 f1 y
  • 指定二进制位数截取- g' I& Y/ X5 N  N5 j8 B  l3 W

比如说16位二进制数A:1001 1001 1001 1000,如果来你想获A的哪一位的值,就把数字B:0000 0000 0000 0000的那一位设置为1.

比如说我想获得A的第三位就把B的第三位数字设置为1,则B为0000 0000 0000 0100,设置完之后再把A、B求与, 其结果若为0,说明A的第三位为0,其结果为1,说明A的第三位为1.

同理:若要获得A的第五位,就把B设置为0000 0000 0001 0000,之后再求与。

通常在我们的程序中,数字B被称为掩码,其含义是专门用来测试某一位是否为0的数值。

  • 统计二进制中 1 的个数9 Z' _1 g, t/ v

利用x=x&(x-1),会将x用二进制表示时最右边的一个1变为0,因为x-1会将该位变为0.

int Count(int x)
0 n. e7 T' Q, z8 ]{   int sum=0;! K6 X. n& F- Q2 X0 e) J
    while(x)
7 j2 N6 E# D. d8 {2 v+ h    {   sum++;2 v5 H& {  I3 y0 o4 S/ S
        x=x&(x-1);
  j9 {2 e1 N* B  B) s7 J; O4 y' ~" ]0 ]    }. L2 a5 Q( w* L( G6 @3 n
    return sum;
, A3 [; K- n7 Q+ P6 a}
9 |! Q, b# \+ T
or操作
  • 生成组合编码,进行状态压缩
    8 q2 L. B3 q  z4 w1 U

当把二进制当作集合使用时,可以用or操作来增加元素。合并编码 在对字节码进行加密时,加密后的两段bit需要重新合并成一个字节,这时就需要使用or操作。

  • 求一个数的二进制表达中0的个数
    2 X# r- i8 u3 b+ z# Q! [
int Grial(int x). ?$ e. @7 P% O8 S1 W1 Y1 c
{0 V3 ]% I- N3 J# P( C! \. M
    int count = 0;5 O  z* U1 k. d* n* o4 c
    while (x + 1)
. _6 n1 |' l3 z: C% Q$ u    {
0 I$ ^8 N% p. P8 b4 q0 i        count++;
7 P! l# ]$ Q1 G1 L7 @        x |= (x + 1);; l2 h/ Y2 e% t5 g
    }: I9 B# I' Y3 Y
    return count;
* `& W9 I& z- B% q( n* \}

/ x1 P  M. ^$ A7 @! m" Xxor操作
  • 两个整数交换变量名
    6 q* [2 N' `+ B% M; R. A6 \
void swap(int &a, int &b) {
6 X: c0 o/ P( s  a ^= b;5 C9 |9 ^/ I- }$ b0 u: |( g8 ]
  b ^= a;3 W4 O9 d& ~9 h* F( j+ L+ @* L7 k
  a ^= b;
) m3 M* G2 \( C  A}

+ C' W( R2 ^8 K' V' i) x& G3 Z6 o
  • 判断两个数是否异号" q9 ?- D) W; O. {# g, X
int x = -1, y = 2;* t7 B" k7 H; ~! U1 s
bool f = ((x ^ y) < 0); // true8 q% }! \( T" s- }5 x. G" q

; N" w4 D/ E$ p( u2 P2 }0 Dint x = 3, y = 2;9 f9 b8 |4 ]1 s8 X; L3 I" z4 K/ V
bool f = ((x ^ y) < 0); // false

" I9 [( y" e% b) q0 V1 {
  • 数据加密
    ; H! l9 y5 K4 ^2 W

将需要加密的内容看做A,密钥看做B,A ^ B=加密后的内容C。而解密时只需要将C ^ 密钥B=原内容A。如果没有密钥,就不能解密!

#include <stdio.h>
2 V8 f2 R$ ?5 S#include <stdlib.h>
" m+ |! P$ H* t7 |1 B4 j$ l  k. B7 H#include <string.h># h( U4 v* Q- z  v
#define KEY 0x86* t* o" i5 M$ H  E1 d
int main()
+ |* T  Z2 z( w  L( q' \# C) q* w{
9 {" S& w3 E- e$ Z. F    char p_data[16] = {"Hello World!"};
$ |$ Q! l  w6 u) U    char Encrypt[16]={0},Decode[16]={0};! j% G7 c3 x3 k1 d# @9 S
    int i;8 q* ~% }9 J( |+ h
       6 T1 R( v5 x5 L! H! E) p0 D  W
    for(i = 0; i < strlen(p_data); i++)5 H2 n% M2 I9 F; o
    {, i- j  G. m: ?# x9 I( F
            Encrypt = p_data ^ KEY;. N3 _0 D/ l% e% T- \
    }. ?6 N! b) M! ?/ r& `+ u$ Z3 s0 b  [
        7 J- ^' y4 z7 p
    for(i = 0; i < strlen(Encrypt); i++)
. X  s( L- D- C( @! \6 h    {
; l: @% T; x( L- H$ @            Decode = Encrypt ^ KEY;0 V+ o5 h( @7 r" k
    }
/ _9 U6 m' k  U        7 s6 |; z5 x: X* _+ G
    printf("Initial date:  %s\n",p_data);( z, Q- E" k9 m# B
    printf("Encrypt date:  %s\n",Encrypt);
! g5 h: m9 j! ~; C' x4 a' t; L    printf("Decode date:  %s\n",Decode);3 N& W1 L" ^7 v6 V  X7 j- l
        
0 {: n4 w4 Y- N; K( I, ]) A    return 0;
" B# Y9 Z4 w5 r1 O' |3 b2 q/ r$ x: M}

2 i, Z* G8 f( I
  • 数字判重
    # A7 {5 k5 l+ V0 ^7 W- H

利用了二进制数的性质:x^y^y = x。我们可见,当同一个数累计进行两次xor操作,相当于自行抵销了,剩下的就是不重复的数

  • 找出没有重复的数
    - R2 f2 ]9 j6 j# M8 k. M+ }
int find(int[] arr){
1 q& t* z/ `* E. Y. v  ~    int tmp = arr[0];
( m2 Z) o* T  A( ~    for(int i = 1;i < arr.length; i++){  T# n# l! D0 X8 Z% \
        tmp = tmp ^ arr;, k) P7 Q. F5 C0 E
    }) \) V" f% \9 T0 I) f% R
    return tmp;
" m; J; Q9 u. s" c1 ^1 i}

9 v6 k# q+ Y3 h: n8 onot操作
  • 交换符号
    5 T5 |3 {2 q! I, c0 y
int reversal(int a) {8 i, A0 x7 P$ Z7 u; k
  return ~a + 1;
2 C7 g) I7 l' r/ c) F5 ?! P}
  t1 o$ I* b+ j7 T5 s& {
  • 取绝对值(效率高). w, f/ ]+ C" ~+ V# {
  • n>>31 取得n的符号
  • 若n为正数,n>>31等于0
  • 若n为负数,n>>31等于-1
  • 若n为正数 n^0=0,数不变
  • 若n为负数,有n^-1 需要计算n和-1的补码,然后进行异或运算,结果n变符号并且为n的绝对值减1,再减去-1就是绝对值
    + v! }, i" ?3 `
int abs(int n)
* D; x) G7 T" q; R0 N+ F# R: Y: G{" z& {5 x% A7 b9 \) Q
  return (n ^ (n >> 31)) - (n >> 31);. [3 T7 `+ a  V4 L3 e
}

& X9 @- ?$ L2 \

也可以这样使用

int abs(int n)
& S$ e2 l8 |+ V$ @{
6 f, A$ a$ V6 }  int i = n >> 31;
, W& z" r; G7 _! P, h! L* N  `  return i == 0 ? n : (~n + 1);
( ^% ^7 N" p! U" R}
/ L- [: J  s! r
  • 从低位到高位.将n的第m位置1$ K7 z" z& x8 _0 O1 q9 h- M* ]

将1左移m-1位找到第m位,得到000...1...000, n在和这个数做或运算

int setBitToOne(int n, int m)
0 n2 e* o  n+ S{
$ M( H/ n; r6 B" Y1 E4 t) y3 V& _# ^7 {    return n | (1 << (m-1));1 G1 z0 Z( F5 w; }; C$ M
}
: i: q7 X+ ~; D- V& ?& A

同理从低位到高位,将n的第m位置0,代码如下

int setBitToZero(int n, int m); f1 a' l1 p$ S2 o( W
{
3 o1 t) p  B. s5 z. D5 v5 P* l+ `    return n & ~(1 << (m-1));
% x" N- H( \' E1 E/ M/ R/ X+ `; K}
# N, ]- e6 ~8 c  D3 `2 o: b! m
shl操作 & shr操作
  • 求2的N次方
    # z! g- o* b3 n$ v0 p* }9 P
1<<n1 D/ t3 r. P) W4 x( I9 o1 Z$ K
  • 高低位交换& b! e" q  s! B" A: o$ t: O
unsigned short a = 34520;
. b4 x$ C0 U- Y2 w2 P5 _+ G& ca = (a >> 8) | (a << 8);
2 x/ A/ q- F1 }  n
  • 进行二进制逆序& P) @' o9 p+ L2 U# a6 `
unsigned short a = 34520;
/ W0 ?: U- j4 u$ c2 i* X0 j
+ e% j& _/ ~/ j! l6 H+ x9 na = ((a & 0xAAAA) >> 1) | ((a & 0x5555) << 1);
/ H8 Y& s9 @" \) S; C5 }. ta = ((a & 0xCCCC) >> 2) | ((a & 0x3333) << 2);
- y9 j# R! a/ d, h* L& B( Sa = ((a & 0xF0F0) >> 4) | ((a & 0x0F0F) << 4);3 M# x: a) p: W1 \& A
a = ((a & 0xFF00) >> 8) | ((a & 0x00FF) << 8);
( U# U2 P5 J0 l( R  Z. O- N! q2 X
  • 获得int型最大最小值
      G! G. K# }' C: ^  j
int getMaxInt()
5 a  T8 g& r$ q{
- d5 P0 k' [. @' R    return (1 << 31) - 1;//2147483647, 由于优先级关系,括号不可省略
5 ^* e5 S( k6 a" c8 V, z: n}
& Q1 h- R* u* v: x8 b9 o0 T
* ]( j4 }8 L! bint getMinInt()2 q$ ]6 f! U* b. ^  y
{
7 p& S% f" S  H( j8 Y    return 1 << 31;//-2147483648
, N0 M6 f2 Z4 M# R# X}

/ \; {+ z# A' k: O2 d
  • m的n次方5 w* l/ ~7 W. I$ r
//自己重写的pow()方法* |3 i3 p$ H- Z& T8 E; P+ U
int pow(int m , int n){
0 |! q+ c9 d3 X    int sum = 1;
5 W& K% n3 Y+ `" [0 b    while(n != 0){# E- G5 R: {4 S* x8 _% J7 u
        if(n & 1 == 1){
/ T9 f1 f  T* n2 T            sum *= m;/ S5 x( ?+ i( n) ?9 ]
        }
. E# g. f3 y* f, _        m *= m;
! @6 Y: y0 v" L& F; F4 M2 q        n = n >> 1;7 `0 s1 f; N: W0 W
    }
; G0 W/ t# c3 U' q* }# A; o' G7 z% ?* r2 c" s
    return sum;
, @1 n+ P" ?# I9 ?5 z. P3 U* ]/ K% F}

9 h; _  E: O! z2 M+ w) ]
  • 找出不大于N的最大的2的幂指数* d3 P/ N! b7 U; b/ ]; e3 P- g' w) S
int findN(int n){
8 C7 e/ R0 T8 G3 j7 ?    n |= n >> 1;( K' |) k6 d2 ?6 u$ C
    n |= n >> 2;! b# }$ e+ _- g: i
    n |= n >> 4;( y$ o* L9 `" r1 l% J3 Q  l# H
    n |= n >> 8 // 整型一般是 32 位,上面我是假设 8 位。& r7 q- B* W4 M; ?# b, E
    return (n + 1) >> 1;3 S& {3 r/ u2 n% J* B/ e. u
}

0 l: k! B; r5 K
  • 二分查找32位整数的前导0个数7 z7 S1 V" p: ^4 k# N0 P5 Y) o$ a$ v
int nlz(unsigned x)9 o2 s! \9 D9 ?/ I7 ]) |
{: i+ v% y+ w4 F8 V
   int n;. L5 m7 K/ k1 B1 i: |; i5 n
( f& x. r# \  z& E
   if (x == 0) return(32);2 T5 L7 Z6 v- k" \  l
   n = 1;$ E7 ^3 e3 T, B2 U9 e% T
   if ((x >> 16) == 0) {n = n +16; x = x <<16;}
: [: {. K- a0 ^0 s. b4 a. C   if ((x >> 24) == 0) {n = n + 8; x = x << 8;}
1 F$ ~* i% u7 |& f8 r6 ~: `   if ((x >> 28) == 0) {n = n + 4; x = x << 4;}
9 {9 L# g$ u$ i2 R   if ((x >> 30) == 0) {n = n + 2; x = x << 2;}
1 I5 [* ?0 I3 D( ^4 Z5 A2 |4 G   n = n - (x >> 31);+ e; v; l( P5 ^/ U# I' j6 e4 o
   return n;
" P7 [/ R& N- b" ~- a}
- @4 l' x% E6 S/ o% L6 a
  • 位图的操作
    4 D! J& ^+ m' j9 n8 v

将 x 的第 n 位置1,可以通过 x |= (x << n) 来实现

set_bit(char x, int n);
, P' l! Y6 V% ^2 |) J9 B: y

将 x 的第 n 位清0,可以通过 x &= ~(1 << n) 来实现

clr_bit(char x, int n);- ?; G. k7 F9 J

取出 x 的第 n 位的值,可以通过 (x >> n) & 1 来实现

get_bit(char x, int n);1 H, `6 ?( S$ ?* F8 ^1 h9 g7 V7 J

如下:

#define clr_bit(x, n) ( (x) &= ~(1 << (n)) )( \6 X  c$ a6 l6 a* ?
#define set_bit(x, n) ( (x) |= (1 << (n)) )
" U: b) i3 e6 Q8 J2 e3 x! `: [3 C#define get_bit(x, n) ( ((x)>>(n)) & 1 )

+ l. n6 \, D- P& M综合应用

以下仅列出,感兴趣可以参考下面链接.

关于操作计数方法

计算整数的符号

检测两个整数是否具有相反的符号

计算无分支的整数绝对值(abs)

计算两个整数的最小值(最小值)或最大值(最大值),而无需分支

确定整数是否为2的幂

标志延伸

  • 从恒定位宽扩展的符号
  • 从可变位宽扩展的符号
  • 通过3个操作从可变位宽扩展符号 有条件地设置或清除位而不分支) [4 |" d$ g, Q  e! [5 y. q6 W, a

有条件地否定一个值而不分支

根据掩码合并两个值中的位

计数位设置

  • 计数位设置,幼稚的方式
  • 计算由查找表设置的位
  • 数位集,Brian Kernighan的方式
  • 使用64位指令对14、24或32位字中设置的位进行计数
  • 并行设置计数位
  • 从最高有效位到给定位置的计数位的设置(等级)
  • 从给定的计数(等级)中选择位位置(从最高有效位开始)
    0 C$ x3 ~6 O. q. I7 `

计算奇偶校验(如果设置了奇数位数,则为1,否则为0)

  • 天真地计算单词的奇偶性
  • 通过查找表计算奇偶校验
  • 使用64位乘法和模数除法计算字节的奇偶校验
  • 用乘法计算单词的奇偶校验
  • 并行计算奇偶校验/ \# [0 l# l2 b6 C2 `4 C2 D

交换价值

  • 用减法和加法交换值
  • 用XOR交换值
  • 用XOR交换单个位
    # k$ `% z: z7 N+ j* ]

反转位序列

  • 反转位是显而易见的方式
  • 逐字查找表中的位反转
  • 通过3个操作(64位乘法和模数除法)反转字节中的位
  • 通过4个操作反转字节中的位(64位乘法,无除法)
  • 通过7个操作反转字节中的位(无64位,仅32位)
  • 与5 * lg(N)个运算并行地反转N位数量: ?# ?% a0 M: s% _  v, v4 P

模数除法(又名计算余数)

  • 在不进行除法运算的情况下,将模数除以1 << s(显而易见)
  • 在不进行除法运算的情况下以(1 << s)-1计算模数除法
  • 不进行除法运算就并行计算(1 << s)-1的模数除法
    5 e9 _; U7 W  V$ i2 }+ `# r

查找整数的整数对数2(又称最高位集的位置)

  • 使用O(N)运算找到MSB N设置为整数的对数2(显而易见的方法)
  • 查找具有64位IEEE浮点数的整数的整数对数2
  • 使用查找表找到整数的对数2
  • 在O(lg(N))运算中找到N位整数的对数2
  • 使用乘法和查找在O(lg(N))操作中找到N位整数的对数2& g4 j" S; Q+ M  H6 W6 e  a

查找整数的对数以10为底的整数

查找整数的整数对数10

查找32位IEEE浮点数的整数对数基数2

查找32位IEEE浮点的pow(2,r)根的整数对数基数2(对于无符号整数r)

计算连续的尾随零位(或查找位索引)

  • 线性计算右边的连续零位(后缀)
  • 并行计算右侧连续的零位(后缀)
  • 通过二进制搜索计算右边连续的零位(跟踪)
  • 通过强制转换为浮点数来计算右侧连续的零位(跟踪)
  • 用模数除法和查找计算右边连续的零位(跟踪)
  • 用乘法和查找计数右边连续的零位(后跟)
    9 i; a& r, a  V0 g8 j$ t9 Z

通过浮法舍入到2的下一个最高幂

向上舍入到2的下一个最高幂

交织位(也称为计算莫顿数)

  • 交错位的明显方式
  • 通过表查找交织位
  • 带64位乘法的交织位
  • 通过二进制幻数交错位
    . }: r! r9 @8 \# O% c" @4 Q  f

测试单词中的字节范围(并计算出现的次数)

  • 确定单词是否为零字节
  • 确定一个单词的字节数是否等于n
  • 确定一个单词的字节数是否小于n
  • 确定单词的字节数是否大于n
  • 确定单词是否在m和n之间有一个字节) B$ x6 q' G8 o" q  D

按词典顺序计算下一位排列


2 c* P' V8 e7 a( D
赞 收藏 评论0 发布时间:2020-6-11 20:35

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版