查看原文
其他

同事代码中的"异或^"操作把我秀翻了~

IT服务圈儿 2022-09-11

The following article is from 最后一个bug Author bug菌

来源丨经授权转自 最后一个bug(ID:unknown_bug)

作者丨bug菌


大家好,我是bug菌!

1

眼中的异或操作

最近看一些开源的代码遇到了异或操作,第一次遇到的时候感觉挺有意思的,毕竟对于ifelse专家而言,这些“|”、“&”等运算符是再熟悉不过了,但是异或运算符估计很多朋友只在初学运算符的章节背过一些性质罢了,所以今天bug菌把异或的一些小技巧分享一下~
一句话总结异或运算:"相同为0,相异为1"
A XOR B    result  
0  ^  0  =  0  
0  ^  1  =  1  
1  ^  0  =  1  
1  ^  1  =  0

当年在学习到这个运算符就在想:前三个操作与"或运算"是一致的,当两者都是1时反而得到0,这运算还真是有点异类~,然后就一笑而过了~
随着自己的码量越来越大,一些大佬的异或操作把我给秀翻了,所以这里bug菌把异或的这些技巧分享给大家~

2

异或神技

1

半加法运算

根据前面的异或运算,相比加法运算而言,异或操作唯一不同的是1^1=0而不是10,也就是说异或操作不会产生进位,那么异或操作结合进位处理就可以等价于加法运算了。
二进制的进位处理便是处理相同位置同时为1的左移问题。
参数Demo:
#include <stdio.h>

/***********************************
 * Function: AddXor
 * Author  : bug菌 
 * Description:异或加法 
 **********************************/
 
int AddXor(int a,int b)
{
    int c = 0;

    c = a^b;        
    b = (a&b)<<1;  //两数相与,如果存在相同位为1,则不为0,左移1位,供下面继续进位处理
    while(b)       //b!=0,表示存在进位需要处理
    {
        a = c;
        c = a^b;
        b = (a&b)<<1
    }
    return c;       
}
/***********************************
 * Function: main
 * Author  : bug菌 
 * Description:主函数
 **********************************/
 
int main(int argc, char *argv[])
{
    int a = 123;
    int b = 55;

    printf("%d + %d = %d\n",a,b,AddXor(a,b));

    printf("欢迎关注:最后一个bug");
    return 0;
}

运算结果:


结果OK,但是我相信你在平时编程中肯定不会这么玩,何必为难自己,这种加法的实现,纯属学习、理解异或就好了。
那异或的半加法运算就没有啥用了?

并不是所有的加法运算都需要进位,比如1024=0x400与一个小于1024的正数之和是不会产生进位的,并且一旦累计进位,相当于在原来的基础上-1024,比如1025^1024=1,可以达到限制数据大小的目的。

2

不用第三变量交换两个数

说到这个其实很早在下面这篇文章中有提到过异或操作方案。
☞【硬核C进阶】如何实现 万能 "两数交换" 宏 ?
A = A ^ B;
B = A ^ B;
A = A ^ B;

当看到这种办法以后,你会拿着两个数据来进行尝试,最后验证OK,以后估计也会不了了之,其实这中间蕴含着异或运算一个非常重要的性质。
当一个数异或同一个数偶数次,最终还是得到原来的数。
当B的个数为偶数,则A^B^B^B.... = A
有了这个性质我们再推导一下两数交换:

    A = A ^ B;   .............(1)
B = A ^ B;   .............(2)
A = A ^ B;   .............(3)

(2) => B = (A ^ B) ^  B  => B = A 
(3) => A = (A ^ B) ^ (A) => A = B

将第(2)和第(3)分别展开便可以得到最终交换结果,秒呀~

3

固定位翻转

点灯大师当累了,可以玩玩LED的翻转,从异或的性质来看它是天然的翻转神器:

0 ^ 1 = 1
1 ^ 1 = 0

比如我们想对一个GPIO的数据寄存器中的1、2、5位进行翻转,你可以定时直接GPIO_DATA^0b100110便可以实现对应位的电平翻转。

当然这里bug菌只是聊了一下翻转LED,思路还可以更加的开阔~

4

不用条件语句实现整数绝对值

怎么说呢?这种编程一般用在面试题里面,实际项目中基本上就是一个abs搞定了,这里主要是借助这个实例来讲讲异或的两个小技巧。

    A ^ 0xFF.. = ~A
A ^ 0      =  A

正数的绝对值为原值,而负数的绝对值为取反+1.

那么当看到上面两个性质,以一个8位有符号数为例子,其最高位为符号位,正数为0,负数为1,那么把一个8位有符号数右移7位。

如果是正数最高位补0,最终数值为0;

如果是负数最高位补1,最终数字为0xFF,也就是-1。

参数Demo:

#include <stdio.h> 

/***********************************
 * Function: abs_s8
 * Author  : bug菌 
 * Description:只能用于8位,32位自行扩展即可 
 **********************************/ 
char abs_s8(char a)
{
    char b = 0;

    b = a >> 7;
    a = (a ^ b) - b;
    return a;
}
/***********************************
 * Function: main
 * Author  : bug菌 
 * Description:主函数 
 **********************************/ 
int main(void)
{
    printf("abs_s8(55)  = %d....\n",abs_s8(55)); 
    printf("abs_s8(-55) = %d....\n",abs_s8(-55)); 
    printf("\n欢迎关注最后一个bug\n"); 
    return 0;
}

运行结果:


确实很多的小技巧透露着计算机的强大设计理念和思想,一个优秀的算法也是建立在一定的平台和条件之上的,所以当我们多去了解程序的运行和运算机理,自然就可以设计出最优秀的代码~

1、SQL:我能玩出这花样儿?

2、推荐一个能够提升编程效率的VS code插件

3、漫画 | 当一个程序员专注数据库11年,可以收获什么?

4、美团外卖小哥竟开发阿里云盘,抢先首发!代码已开源,收割600星

点分享

点点赞

点在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存