`
美丽的小岛
  • 浏览: 308323 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

位运算<转>

 
阅读更多

什么是位运算?
    程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理):
     110
AND 1011
----------
    0010  -->  2

    由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快。当然有人会说,这个快了有什么用,计算6 and 11没有什么实际意义啊。这一系列的文章就将告诉你,位运算到底可以干什么,有些什么经典应用,以及如何用位运算优化你的程序。


Pascal和C中的位运算符号
    下面的a和b都是整数类型,则:
C语言  |  Pascal语言
-------+-------------
a & b  |  a and b
a | b  |  a or b
a ^ b  |  a xor b
  ~a   |   not a
a << b |  a shl b
a >> b |  a shr b

    注意C中的逻辑运算和位运算符号是不同的。520|1314=1834,但520||1314=1,因为逻辑运算时520和1314都相当于True。同样的,!a和~a也是有区别的。


各种位运算的使用
    === 1. and运算 ===
    and运算通常用于二进制取位操作,例如一个数 and 1的结果就是取二进制的最末位。这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为偶数,最末位为1表示该数为奇数.

    === 2. or运算 ===
    or运算通常用于二进制特定位上的无条件赋值,例如一个数or 1的结果就是把二进制最末位强行变成1。如果需要把二进制最末位变成0,对这个数or 1之后再减一就可以了,其实际意义就是把这个数强行变成最接近的偶数。

    === 3. xor运算 ===
    xor运算通常用于对二进制的特定一位进行取反操作,因为异或可以这样定义:0和1异或0都不变,异或1则取反。
    xor运算的逆运算是它本身,也就是说两次异或同一个数最后结果不变,即(a xor b) xor b = a。xor运算可以用于简单的加密,比如我想对我MM说1314520,但怕别人知道,于是双方约定拿我的生日19880516作为密钥。1314520 xor 19880516 = 20665500,我就把20665500告诉MM。MM再次计算20665500 xor 19880516的值,得到1314520,于是她就明白了我的企图。
    下面我们看另外一个东西。定义两个符号#和@(我怎么找不到那个圈里有个叉的字符),这两个符号互为逆运算,也就是说(x # y) @ y = x。现在依次执行下面三条命令,结果是什么?
x <- x # y
y <- x @ y
x <- x @ y

    执行了第一句后x变成了x # y。那么第二句实质就是y <- x # y @ y,由于#和@互为逆运算,那么此时的y变成了原来的x。第三句中x实际上被赋值为(x # y) @ x,如果#运算具有交换律,那么赋值后x就变成最初的y了。这三句话的结果是,x和y的位置互换了。
    加法和减法互为逆运算,并且加法满足交换律。把#换成+,把@换成-,我们可以写出一个不需要临时变量的swap过程(Pascal)。
procedure swap(var a,b:longint);
begin
   a:=a + b;
   b:=a - b;
   a:=a - b;
end;

    好了,刚才不是说xor的逆运算是它本身吗?于是我们就有了一个看起来非常诡异的swap过程:
procedure swap(var a,b:longint);
begin
   a:=a xor b;
   b:=a xor b;
   a:=a xor b;
end;


    === 4. not运算 ===
    not运算的定义是把内存中的0和1全部取反。使用not运算时要格外小心,你需要注意整数类型有没有符号。如果not的对象是无符号整数(不能表示负数),那么得到的值就是它与该类型上界的差,因为无符号类型的数是用$0000到$FFFF依次表示的。下面的两个程序(仅语言不同)均返回65435。
var
   a:word;
begin
   a:=100;
   a:=not a;
   writeln(a);
end.

#include <stdio.h>
int main()
{
    unsigned short a=100;
    a = ~a;
    printf( "%d\n", a );    
    return 0;
}

    如果not的对象是有符号的整数,情况就不一样了,稍后我们会在“整数类型的储存”小节中提到。

    === 5. shl运算 ===
    a shl b就表示把a转为二进制后左移b位(在后面添b个0)。例如100的二进制为1100100,而110010000转成十进制是400,那么100 shl 2 = 400。可以看出,a shl b的值实际上就是a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2。
    通常认为a shl 1比a * 2更快,因为前者是更底层一些的操作。因此程序中乘以2的操作请尽量用左移一位来代替。
    定义一些常量可能会用到shl运算。你可以方便地用1 shl 16 - 1来表示65535。很多算法和数据结构要求数据规模必须是2的幂,此时可以用shl来定义Max_N等常量。

    === 6. shr运算 ===
    和shl相似,a shr b表示二进制右移b位(去掉末b位),相当于a除以2的b次方(取整)。我们也经常用shr 1来代替div 2,比如二分查找、堆的插入操作等等。想办法用shr代替除法运算可以使程序效率大大提高。最大公约数的二进制算法用除以2操作来代替慢得出奇的mod运算,效率可以提高60%。


位运算的简单应用
    有时我们的程序需要一个规模不大的Hash表来记录状态。比如,做数独时我们需要27个Hash表来统计每一行、每一列和每一个小九宫格里已经有哪些数了。此时,我们可以用27个小于2^9的整数进行记录。例如,一个只填了2和5的小九宫格就用数字18表示(二进制为000010010),而某一行的状态为511则表示这一行已经填满。需要改变状态时我们不需要把这个数转成二进制修改后再转回去,而是直接进行位操作。在搜索时,把状态表示成整数可以更好地进行判重等操作这是在搜索中使用位运算加速的经典例子。以后我们会看到更多的例子。
    下面列举了一些常见的二进制位的变换操作。

    功能              |           示例            |    位运算
----------------------+---------------------------+--------------------
去掉最后一位          | (101101->10110)           | x shr 1
在最后加一个0         | (101101->1011010)         | x shl 1
在最后加一个1         | (101101->1011011)         | x shl 1+1
把最后一位变成1       | (101100->101101)          | x or 1
把最后一位变成0       | (101101->101100)          | x or 1-1
最后一位取反          | (101101->101100)          | x xor 1
把右数第k位变成1      | (101001->101101,k=3)      | x or (1 shl (k-1))
把右数第k位变成0      | (101101->101001,k=3)      | x and not (1 shl (k-1))
右数第k位取反         | (101001->101101,k=3)      | x xor (1 shl (k-1))
取末三位              | (1101101->101)            | x and 7
取末k位               | (1101101->1101,k=5)       | x and (1 shl k-1)
取右数第k位           | (1101101->1,k=4)          | x shr (k-1) and 1
把末k位变成1          | (101001->101111,k=4)      | x or (1 shl k-1)
末k位取反             | (101001->100110,k=4)      | x xor (1 shl k-1)
把右边连续的1变成0    | (100101111->100100000)    | x and (x+1)
把右起第一个0变成1    | (100101111->100111111)    | x or (x+1)
把右边连续的0变成1    | (11011000->11011111)      | x or (x-1)
取右边连续的1         | (100101111->1111)         | (x xor (x+1)) shr 1
去掉右起第一个1的左边 | (100101000->1000)         | x and (x xor (x-1))


    最后这一个在树状数组中会用到。


Pascal和C中的16进制表示
    Pascal中需要在16进制数前加$符号表示,C中需要在前面加0x来表示。这个以后我们会经常用到。

整数类型的储存
    我们前面所说的位运算都没有涉及负数,都假设这些运算是在unsigned/word类型(只能表示正数的整型)上进行操作。但计算机如何处理有正负符号的整数类型呢?下面两个程序都是考察16位整数的储存方式(只是语言不同)。
var
   a,b:integer;
begin
   a:=$0000;
   b:=$0001;
   write(a,' ',b,' ');
   a:=$FFFE;
   b:=$FFFF;
   write(a,' ',b,' ');
   a:=$7FFF;
   b:=$8000;
   writeln(a,' ',b);
end.

#include <stdio.h>
int main()
{
    short int a, b;
    a = 0x0000;
    b = 0x0001;
    printf( "%d %d ", a, b );
    a = 0xFFFE;
    b = 0xFFFF;
    printf( "%d %d ", a, b );
    a = 0x7FFF;
    b = 0x8000;
    printf( "%d %d\n", a, b );
    return 0;
}

    两个程序的输出均为0 1 -2 -1 32767 -32768。其中前两个数是内存值最小的时候,中间两个数则是内存值最大的时候,最后输出的两个数是正数与负数的分界处。由此你可以清楚地看到计算机是如何储存一个整数的:计算机用$0000到$7FFF依次表示0到32767的数,剩下的$8000到$FFFF依次表示-32768到-1的数。32位有符号整数的储存方式也是类似的。稍加注意你会发现,二进制的第一位是用来表示正负号的,0表示正,1表示负。这里有一个问题:0本来既不是正数,也不是负数,但它占用了$0000的位置,因此有符号的整数类型范围中正数个数比负数少一个。对一个有符号的数进行not运算后,最高位的变化将导致正负颠倒,并且数的绝对值会差1。也就是说,not a实际上等于-a-1。这种整数储存方式叫做“补码”。
 ~~~~====~~~~=====   华丽的分割线   =====~~~~====~~~~

二进制中的1有奇数个还是偶数个
    我们可以用下面的代码来计算一个32位整数的二进制中1的个数的奇偶性,当输入数据的二进制表示里有偶数个数字1时程序输出0,有奇数个则输出1。例如,1314520的二进制101000000111011011000中有9个1,则x=1314520时程序输出1。
var
   i,x,c:longint;
begin
   readln(x);
   c:=0;
   for i:=1 to 32 do
   begin
      c:=c + x and 1;
      x:=x shr 1;
   end;
   writeln( c and 1 );
end.

    但这样的效率并不高,位运算的神奇之处还没有体现出来。
    同样是判断二进制中1的个数的奇偶性,下面这段代码就强了。你能看出这个代码的原理吗?
var
   x:longint;
begin
   readln(x);
   x:=x xor (x shr 1);
   x:=x xor (x shr 2);
   x:=x xor (x shr 4);
   x:=x xor (x shr 8);
   x:=x xor (x shr 16);
   writeln(x and 1);
end.

    为了说明上面这段代码的原理,我们还是拿1314520出来说事。1314520的二进制为101000000111011011000,第一次异或操作的结果如下:

    00000000000101000000111011011000
XOR  0000000000010100000011101101100
---------------------------------------
    00000000000111100000100110110100


    得到的结果是一个新的二进制数,其中右起第i位上的数表示原数中第i和i+1位上有奇数个1还是偶数个1。比如,最右边那个0表示原数末两位有偶数个1,右起第3位上的1就表示原数的这个位置和前一个位置中有奇数个1。对这个数进行第二次异或的结果如下:

    00000000000111100000100110110100
XOR   000000000001111000001001101101
---------------------------------------
    00000000000110011000101111011001


    结果里的每个1表示原数的该位置及其前面三个位置中共有奇数个1,每个0就表示原数对应的四个位置上共偶数个1。一直做到第五次异或结束后,得到的二进制数的最末位就表示整个32位数里有多少个1,这就是我们最终想要的答案。

计算二进制中的1的个数
    同样假设x是一个32位整数。经过下面五次赋值后,x的值就是原数的二进制表示中数字1的个数。比如,初始时x为1314520(网友抓狂:能不能换一个数啊),那么最后x就变成了9,它表示1314520的二进制中有9个1。
x := (x and $55555555) + ((x shr 1) and $55555555);
x := (x and $33333333) + ((x shr 2) and $33333333);
x := (x and $0F0F0F0F) + ((x shr 4) and $0F0F0F0F);
x := (x and $00FF00FF) + ((x shr 8) and $00FF00FF);
x := (x and $0000FFFF) + ((x shr 16) and $0000FFFF);

    为了便于解说,我们下面仅说明这个程序是如何对一个8位整数进行处理的。我们拿数字211(我们班某MM的生日)来开刀。211的二进制为11010011。

+---+---+---+---+---+---+---+---+
| 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 |   <---原数
+---+---+---+---+---+---+---+---+
|  1 0  |  0 1  |  0 0  |  1 0  |   <---第一次运算后
+-------+-------+-------+-------+
|    0 0 1 1    |    0 0 1 0    |   <---第二次运算后
+---------------+---------------+
|        0 0 0 0 0 1 0 1        |   <---第三次运算后,得数为5
+-------------------------------+


    整个程序是一个分治的思想。第一次我们把每相邻的两位加起来,得到每两位里1的个数,比如前两位10就表示原数的前两位有2个1。第二次我们继续两两相加,10+01=11,00+10=10,得到的结果是00110010,它表示原数前4位有3个1,末4位有2个1。最后一次我们把0011和0010加起来,得到的就是整个二进制中1的个数。程序中巧妙地使用取位和右移,比如第二行中$33333333的二进制为00110011001100....,用它和x做and运算就相当于以2为单位间隔取数。shr的作用就是让加法运算的相同数位对齐。
 ~~~~====~~~~=====   华丽的分割线   =====~~~~====~~~~
二分查找32位整数的前导0个数
    这里用的C语言,我直接Copy的Hacker's Delight上的代码。这段代码写成C要好看些,写成Pascal的话会出现很多begin和end,搞得代码很难看。程序思想是二分查找,应该很简单,我就不细说了。
int nlz(unsigned x)
{
   int n;

   if (x == 0) return(32);
   n = 1;
   if ((x >> 16) == 0) {n = n +16; x = x <<16;}
   if ((x >> 24) == 0) {n = n + 8; x = x << 8;}
   if ((x >> 28) == 0) {n = n + 4; x = x << 4;}
   if ((x >> 30) == 0) {n = n + 2; x = x << 2;}
   n = n - (x >> 31);
   return n;
}

 ~~~~====~~~~=====   华丽的分割线   =====~~~~====~~~~
只用位运算来取绝对值
    这是一个非常有趣的问题。大家先自己想想吧,Ctrl+A显示答案。
    答案:假设x为32位整数,则x xor (not (x shr 31) + 1) + x shr 31的结果是x的绝对值
    x shr 31是二进制的最高位,它用来表示x的符号。如果它为0(x为正),则not (x shr 31) + 1等于$00000000,异或任何数结果都不变;如果最高位为1(x为负),则not (x shr 31) + 1等于$FFFFFFFF,x异或它相当于所有数位取反,异或完后再加一。

 ~~~~====~~~~=====   华丽的分割线   =====~~~~====~~~~
高低位交换
    这个题 实际上是我出的,做为学校内部NOIp模拟赛的第一题。题目是这样:
    给出一个小于2^32的正整数。这个数可以用一个32位的二进制数表示(不足32位用0补足)。我们称这个二进制数的前16位为“高位”,后16位为“低位”。将它的高低位交换,我们可以得到一个新的数。试问这个新的数是多少(用十进制表示)。
  例如,数1314520用二进制表示为0000 0000 0001 0100 0000 1110 1101 1000(添加了11个前导0补足为32位),其中前16位为高位,即0000 0000 0001 0100;后16位为低位,即0000 1110 1101 1000。将它的高低位进行交换,我们得到了一个新的二进制数0000 1110 1101 1000 0000 0000 0001 0100。它即是十进制的249036820。

 


    当时几乎没有人想到用一句位操作来代替冗长的程序。使用位运算的话两句话就完了。
var
   n:dword;
begin
   readln( n );
   writeln( (n shr 16) or (n  shl 16) );
end.

    而事实上,Pascal有一个系统函数swap直接就可以用。
 ~~~~====~~~~=====   华丽的分割线   =====~~~~====~~~~
二进制逆序
    下面的程序读入一个32位整数并输出它的二进制倒序后所表示的数。
    输入: 1314520    (二进制为00000000000101000000111011011000)
    输出: 460335104  (二进制为00011011011100000010100000000000)
var
   x:dword;
begin
   readln(x);
   x := (x and $55555555) shl  1 or (x and $AAAAAAAA) shr  1;
   x := (x and $33333333) shl  2 or (x and $CCCCCCCC) shr  2;
   x := (x and $0F0F0F0F) shl  4 or (x and $F0F0F0F0) shr  4;
   x := (x and $00FF00FF) shl  8 or (x and $FF00FF00) shr  8;
   x := (x and $0000FFFF) shl 16 or (x and $FFFF0000) shr 16;
   writeln(x);
end.

    它的原理和刚才求二进制中1的个数那个例题是大致相同的。程序首先交换每相邻两位上的数,以后把互相交换过的数看成一个整体,继续进行以2位为单位、以4位为单位的左右对换操作。我们再次用8位整数211来演示程序执行过程:
+---+---+---+---+---+---+---+---+
| 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 |   <---原数
+---+---+---+---+---+---+---+---+
|  1 1  |  1 0  |  0 0  |  1 1  |   <---第一次运算后
+-------+-------+-------+-------+
|    1 0 1 1    |    1 1 0 0    |   <---第二次运算后
+---------------+---------------+
|        1 1 0 0 1 0 1 1        |   <---第三次运算后
+-------------------------------+

 ~~~~====~~~~=====   华丽的分割线   =====~~~~====~~~~
Gray码
    假如我有4个潜在的GF,我需要决定最终到底和谁在一起。一个简单的办法就是,依次和每个MM交往一段时间,最后选择给我带来的“满意度”最大的MM。但看了dd牛的理论 后,事情开始变得复杂了:我可以选择和多个MM在一起。这样,需要考核的状态变成了2^4=16种(当然包括0000这一状态,因为我有可能是玻璃)。现在的问题就是,我应该用什么顺序来遍历这16种状态呢?
    传统的做法是,用二进制数的顺序来遍历所有可能的组合。也就是说,我需要以0000->0001->0010->0011->0100->...->1111这样的顺序对每种状态进行测试。这个顺序很不科学,很多时候状态的转移都很耗时。比如从0111到1000时我需要暂时甩掉当前所有的3个MM,然后去把第4个MM。同时改变所有MM与我的关系是一件何等巨大的工程啊。因此,我希望知道,是否有一种方法可以使得,从没有MM这一状态出发,每次只改变我和一个MM的关系(追或者甩),15次操作后恰好遍历完所有可能的组合(最终状态不一定是1111)。大家自己先试一试看行不行。
    解决这个问题的方法很巧妙。我们来说明,假如我们已经知道了n=2时的合法遍历顺序,我们如何得到n=3的遍历顺序。显然,n=2的遍历顺序如下:

00
01
11
10

    你可能已经想到了如何把上面的遍历顺序扩展到n=3的情况。n=3时一共有8种状态,其中前面4个把n=2的遍历顺序照搬下来,然后把它们对称翻折下去并在最前面加上1作为后面4个状态:

0 00
0 01
0 11
0 10   ↑
--------
1 10   ↓
1 11
1 01
1 00

    用这种方法得到的遍历顺序显然符合要求。首先,上面8个状态恰好是n=3时的所有8种组合,因为它们是在n=2的全部四种组合的基础上考虑选不选第3个元素所得到的。然后我们看到,后面一半的状态应该和前面一半一样满足“相邻状态间仅一位不同”的限制,而“镜面”处则是最前面那一位数不同。再次翻折三阶遍历顺序,我们就得到了刚才的问题的答案:

0000
0001
0011
0010
0110
0111
0101
0100
1100
1101
1111
1110
1010
1011
1001
1000

    这种遍历顺序作为一种编码方式存在,叫做Gray码(写个中文让蜘蛛来抓:格雷码)。它的应用范围很广。比如,n阶的Gray码相当于在n维立方体上的Hamilton回路,因为沿着立方体上的边走一步,n维坐标中只会有一个值改变。再比如,Gray码和Hanoi塔问题等价。Gray码改变的是第几个数,Hanoi塔就该移动哪个盘子。比如,3阶的Gray码每次改变的元素所在位置依次为1-2-1-3-1-2-1,这正好是3阶Hanoi塔每次移动盘子编号。如果我们可以快速求出Gray码的第n个数是多少,我们就可以输出任意步数后Hanoi塔的移动步骤。现在我告诉你,Gray码的第n个数(从0算起)是n xor (n shr 1),你能想出来这是为什么吗?先自己想想吧。

    下面我们把二进制数和Gray码都写在下面,可以看到左边的数异或自身右移的结果就等于右边的数。

二进制数   Gray码
   000       000
   001       001
   010       011
   011       010
   100       110
   101       111
   110       101
   111       100


    从二进制数的角度看,“镜像”位置上的数即是对原数进行not运算后的结果。比如,第3个数010和倒数第3个数101的每一位都正好相反。假设这两个数分别为x和y,那么x xor (x shr 1)和y xor (y shr 1)的结果只有一点不同:后者的首位是1,前者的首位是0。而这正好是Gray码的生成方法。这就说明了,Gray码的第n个数确实是n xor (n shr 1)。

    今年四月份mashuo 给我看了这道题 ,是二维意义上的Gray码。题目大意是说,把0到2^(n+m)-1的数写成2^n * 2^m的矩阵,使得位置相邻两数的二进制表示只有一位之差。答案其实很简单,所有数都是由m位的Gray码和n位Gray码拼接而成,需要用左移操作和or运算完成。完整的代码如下:
var
   x,y,m,n,u:longint;
begin
   readln(m,n);
   for x:=0 to 1 shl m-1 do begin
      u:=(x xor (x shr 1)) shl n; //输出数的左边是一个m位的Gray码
      for y:=0 to 1 shl n-1 do
         write(u or (y xor (y shr 1)),' '); //并上一个n位Gray码
      writeln;
   end;
end.


Matrix67原创
转自:
http://www.matrix67.com/blog/archives/266

分享到:
评论

相关推荐

    C#编程经验技巧宝典

    58&lt;br&gt;&lt;br&gt;0081 文本中首字母改为大写 59&lt;br&gt;&lt;br&gt;0082 C#随机数的产生 59&lt;br&gt;&lt;br&gt;0083 身份证从15位升至18位算法 60&lt;br&gt;&lt;br&gt;0084 十进制数转二进制数的算法 60&lt;br&gt;&lt;br&gt;0085 十进制数转八进制数的算法 61...

    C和C++程序设计教程课件

    第1章 C语言概述 &lt;br&gt;第2章 C的基本数据类型及运算 &lt;br&gt;第3章 语句和流程控制 &lt;br&gt;第4章 函数 &lt;br&gt;第5章 变量的存储类别和作用域 &lt;br&gt;第6章 数组 &lt;br&gt;第7章 预处理&lt;br&gt;第8章 指针&lt;br&gt;第9章 位运算、结构体和联合 &lt;br...

    MYSQL

    &lt;br&gt;7.3.10 多列索引 &lt;br&gt;7.3.11 使用来自其他数据库引擎的列类型 &lt;br&gt;7.4 用在SELECT和WHERE子句的函数 &lt;br&gt;7.4.1 分组函数 &lt;br&gt;7.4.2 常用的算术运算 &lt;br&gt;7.4.3 位函数 &lt;br&gt;7.4.4 逻辑运算 &lt;br&gt;7.4.5 比较运算符 ...

    HugeCalc 超大整数完全精度快速计算器/算法库 V7.0.0.0

    &lt;br&gt;&lt;br&gt; 现已提供了如下功能:&lt;br&gt;&lt;br&gt; ⊙ 高精度快速加法&lt;br&gt; ⊙ 高精度快速减法&lt;br&gt; ⊙ 高精度快速乘法&lt;br&gt; ⊙ 高精度快速除法&lt;br&gt; ⊙ 高精度快速同余&lt;br&gt; ⊙ 高精度快速位运算&lt;br&gt; ⊙ 高精度快速乘方&lt;br&gt; ⊙ 高...

    NMEA0183标准输出——GPS数据格式标准[借鉴].pdf

    * GPGSA:GPS精度指针及使用卫星格式,格式为$GPGSA,&lt;1&gt;,&lt;2&gt;,&lt;3&gt;,&lt;4&gt;,&lt;5&gt;,&lt;6&gt;,&lt;7&gt;,&lt;8&gt;,&lt;9&gt;,&lt;10&gt;,&lt;11&gt;,&lt;12&gt;,&lt;13&gt;,&lt;14&gt;,&lt;15&gt;,&lt;16&gt;,&lt;17&gt;*&lt;18&gt;&lt;CR&gt;&lt;LF&gt;,其中: + &lt;1&gt;是模式,M=手动,A=自动 + &lt;2&gt;是模式,1=未定位,2...

    NMEA-0183协议介绍

    * 帧结束:&lt;CR&gt;&lt;LF&gt; GPS 固定数据输出语句 GPGGA: * UTC 时间:&lt;1&gt;,格式为 hhmmss.sss * 纬度:&lt;2&gt;,格式为 ddmm.mmmm(前导位数不足则补 0) * 纬度半球:&lt;3&gt;,N 或 S(北纬或南纬) * 经度:&lt;4&gt;,格式为 dddmm...

    谭浩强C语言教程Word版

    15&lt;br/&gt;11.11 类型定义符typedef 16&lt;br/&gt;12 位运算 1&lt;br/&gt;12.1 位运算符C语言提供了六种位运算符: 1&lt;br/&gt;12.1.1 按位与运算 1&lt;br/&gt;12.1.2 按位或运算 2&lt;br/&gt;12.1.3 按位异或运算 2&lt;br/&gt;12.1.4 求反运算 3&lt;br/&gt;...

    二进制计算器 BinCalc

    自己很早以前写的一个二进制计算器:Megatops BinCalc。&lt;br&gt;支持在Windows 2000/XP/2003上...数据输入和计算完全支持键盘操作,并具备能够进行位运算和算术运算的RPN计算器。&lt;br&gt;&lt;br&gt;均为绿色软件,无需安装即可使用。

    c++头文件成列解释大全

    1. `&lt;bitset&gt;`:提供STL中的位集容器,用于存储和操作位字段。 2. `&lt;cctype&gt;`:包含处理字符的函数,如isalpha()、isdigit()等,用于检查字符的类型。 3. `&lt;cerrno&gt;`:定义了错误码,如errno,可以在出现错误时...

    c++位运算c++位运算

    ### C++位运算详解 #### 一、位运算基础 位运算是计算机科学中的一个重要概念,在C++等编程语言中有着广泛的应用。位运算主要针对二进制位进行操作,通过对单个比特位或者比特位组合的操作来实现特定的功能。 ###...

    C语言头文件介绍大全

    * &lt;complex.h&gt;:支持复数算术运算 * &lt;ctype.h&gt;:字符类型 * &lt;errno.h&gt;:出错码 * &lt;fenv.h&gt;:浮点环境 * &lt;float.h&gt;:浮点常量 * &lt;inttypes.h&gt;:整型格式转换 * &lt;iso646.h&gt;:替代关系操作符宏 * &lt;limits.h&gt;:实现常量 ...

    MySQL中文参考手册.chm

    &lt;br/&gt;7.3.9 列索引 &lt;br/&gt;7.3.10 多列索引 &lt;br/&gt;7.3.11 使用来自其他数据库引擎的列类型 &lt;br/&gt;7.4 用在SELECT和WHERE子句的函数&lt;br/&gt;7.4.1 分组函数 &lt;br/&gt;7.4.2 常用的算术运算 &lt;br/&gt;7.4.3 位函数 &lt;br/&gt;7.4.4 逻辑...

    C/C++头文件一览

    - **`&lt;bitset&gt;`**:位集,用于高效存储和操作位集合。 - **`&lt;deque&gt;`**:双端队列,可以在两端高效插入和删除元素。 - **`&lt;exception&gt;`**:异常处理机制。 - **`&lt;functional&gt;`**:函数对象,用于创建可调用的对象。 ...

    C++实现十进制转二进制运算

    std::cout &lt;&lt; "十进制 " &lt;&lt; decNum &lt;&lt; " 转换为二进制是: " &lt;&lt; decimalToBinary(decNum) &lt;&lt; std::endl; return 0; } ``` 这里,我们使用了`std::bitset`库来方便地进行转换,`to_string()`函数用于将二进制位转换...

    C#位运算及运算符

    C#位运算及运算符 C#位运算是指对整型运算对象按位进行逻辑运算的操作。在C#中,位运算符有六种:位逻辑非运算、位逻辑与运算、位逻辑或运算、位逻辑异或运算、位左移运算和位右移运算。 1. 位逻辑非运算 位逻辑...

    详尽C++头文件库查找方便快捷

    - **`&lt;bitset&gt;`**:位集容器。 #### 算法和通用函数头文件 为了简化代码编写并提高效率,C++提供了一系列算法和通用函数: - **`&lt;algorithm&gt;`**:定义了许多常见的算法,如排序、搜索等。 - **`&lt;functional&gt;`**:...

    C++头文件总结

    - **`&lt;bitset&gt;`**: 支持位集的操作。 - **`&lt;limits.h&gt;`**: 定义了一些类型的最大最小值,但已被`&lt;limits&gt;`取代。 通过上述总结,我们可以看到C++标准库提供了非常丰富的功能支持,涵盖了从基本输入输出到复杂的数据...

    C和C++头文件对比一览

    - **`&lt;bitset&gt;`**:提供了位集容器,可以高效地存储和操作位序列。 - **`&lt;cctype&gt;`**:字符分类函数,与C语言中的`&lt;ctype.h&gt;`相似。 - **`&lt;cerrno&gt;`**:错误码处理,与C语言中的`&lt;errno.h&gt;`相似。 - **`&lt;clocale&gt;`**...

    ARM矢量浮点指令集参考手册

    - **定点到浮点**:`VCVT{C}.&lt;P&gt;.&lt;type&gt;Fd,Fd,#&lt;fbits&gt;`,将定点数转换为浮点数。 - **浮点到定点**:`VCVT{C}.&lt;type&gt;.&lt;P&gt;Fd,Fd,#&lt;fbits&gt;`,将浮点数转换为定点数。 #### 半精度转换 - **单精度到半精度**:`VCVTT...

    位运算符,位运算,位段

    位运算在计算机科学中扮演着重要的角色,尤其是在底层系统编程和硬件交互时。位运算符允许程序员直接操作数据的二进制位,从而实现高效的数据处理和计算。本文将详细介绍位运算符,位运算,以及位段的概念。 首先,...

Global site tag (gtag.js) - Google Analytics