• 位运算实现四则运算(C++实现)


    前言

    Leetcode中有一道这样的题:给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。返回被除数 dividend 除以除数 divisor 得到的商。
    如果正常的四则运算符号不允许使用,呢这道题的考点我觉得应该是位运算来实现,遇到了就好好复习一下,下面将介绍二进制实现四则运算:

    二进制码

    位运算是基于二进制运算的,实际上目前的计算机都只识别二进制码,我们所写的一切指令事实上都是一串010101数字、传输数据也是按比特流的形式传输的。所以先介绍二进制码:

    原码

    最高位表示符号位(0代表正数,1代表负数)。剩下的位数,是这个数的绝对值的二进制。
    比如 一个int变量大小为4字节,在32位的编译器中的二进制表示就是00000000 00000000 00000000 0000000

    10 的原码  00000000 00000000 00000000 00001010
    −10的原码 10000000 00000000 00000000 00001010
    

    反码

    正数的反码和其原码是一样的
    负数的反码就是在其原码的基础上 符号位不变 其他位取反。

    10的反码:  00000000 00000000 00000000 00001010
    −10的反码:11111111  11111111 11111111 11110101
    

    补码

    正数的补码就是其原码
    负数的补码就是在其反码的基础上+1

    10的补码:00000000 00000000 0000000 00001010 
    −10的补码:11111111 11111111 11111111 11110110
    

    在计算机系统中,数值一律用补码来表示:因为补码可以是符号位和数值位统一处理,同时可以试减法按照加法来处理。

    位运算加法

    0111 ^ 0101 = 0010; //结果的每一位等于对应位相加模二,刚好是不带进位的加法结果。
    0111 & 0101 = 0101; //结果的1表示对应位相加为2,0表示对应位相加小于二,刚好是进位标识。
    

    所以有:

    int add(int a, int b)
    {
    	return (b == 0) ? a : add(a^b, (a&b) << 1);
    }
    

    位运算减法

    减法其实就是加上这个数的相反数,这个数原来是用正数的补码表示的,现在变成负数的补码形式了:所以只需要将这个数每一位取反再末尾家一就行了:

    int subtraction(int a, int b)
    {
    	b = add(~b, 1);
    	return add(a, b);
    }
    

    位运算乘法

    对于a * b,每次只需要将a左移一位乘上b的对应位,然后同上一次的结果做加法即可。
    当b的对应位为1时,对a左移一位然后同上一次的结果做加法;如果b的对应位为0,只对a左移一位。

    int getsign(int n)
    {
    	return n >> 31;
    }
    
    int positive(int n)
    {
    	return (getsign(n) & 1) ? add(~n,1): n;
    }
    
    int multiply(int a, int b)
    {
    	bool flag = (getsign(a) ^ getsign(b)) ? 1 : 0;
    	a = positive(a);
    	b = positive(b);
    
    	int res = 0;
    	while (b) {
    		if (b & 1)
    			res = add(res, a);   //只有当前b末尾为1时才运算
    		a = a << 1;  
    		b = b >> 1;
    	}
    	if (flag)
    		add(~res, 1);
    
    	return res;
    }
    

    位运算除法

    同乘法一样,除法也可以进行二进制笔算,以a / b为例,只有当a >= b时才可以上商,又因为是二进制,所以商每次只会多1,在每次上1之后a都要减去一次b。

    int divide(int a, int b)
    {
    	if (b == 0)
    		throw runtime_error("DIVIDED CANNOT BE 0");
    
    	bool flag = (getsign(a) ^ getsign(b)) ? 1 : 0;
    	a = positive(a);
    	b = positive(b);
    
    	int res = 0;
    	while (a >= b)
    	{
    		res = add(res, 1);
    		a = subtraction(a, b);
    	}
    	return flag ? add(~res, 1) : res;
    }
    

    总代码如下:

    int add(int a, int b)
    {
    	return (b == 0) ? a : add(a^b, (a&b) << 1);
    }
    
    int subtraction(int a, int b)
    {
    	b = add(~b, 1);
    	return add(a, b);
    }
    
    int getsign(int n)
    {
    	return n >> 31;
    }
    
    int positive(int n)
    {
    	return (getsign(n) & 1) ? add(~n,1): n;
    }
    
    int multiply(int a, int b)
    {
    	bool flag = (getsign(a) ^ getsign(b)) ? 1 : 0;
    	a = positive(a);
    	b = positive(b);
    
    	int res = 0;
    	while (b) {
    		if (b & 1)
    			res = add(res, a);   //只有当前b末尾为1时才运算
    		a = a << 1;  
    		b = b >> 1;
    	}
    	if (flag)
    		add(~res, 1);
    
    	return res;
    }
    
    int divide(int a, int b)
    {
    	if (b == 0)
    		throw runtime_error("DIVIDED CANNOT BE 0");
    
    	bool flag = (getsign(a) ^ getsign(b)) ? 1 : 0;
    	a = positive(a);
    	b = positive(b);
    
    	int res = 0;
    	while (a >= b)
    	{
    		res = add(res, 1);
    		a = subtraction(a, b);
    	}
    	return flag ? add(~res, 1) : res;
    }
    
  • 相关阅读:
    给JavaScript新手的24条实用建议
    javascript之HTML(select option)详解
    PHP的正则处理函数总结分析
    多级关联菜单:
    理解json两种结构:数组和对象
    dede标签学习笔记(一)
    Jewel_M PHP定时执行任务的实现
    网站刷新器
    PHP_SELF、 SCRIPT_NAME、 REQUEST_URI区别
    RemoveXSS()
  • 原文地址:https://www.cnblogs.com/yunlambert/p/9845897.html
Copyright © 2020-2023  润新知