前言

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

位运算实现四则运算(C++实现)的更多相关文章

  1. 用位运算实现四则运算之加减乘除(用位运算求一个数的1/3) via Hackbuteer1

    转自:http://blog.csdn.net/hackbuteer1/article/details/7390093 ^: 按位异或:&:按位与: | :按位或 计算机系统中,数值一律用补码 ...

  2. 用Java位运算实现加减乘除四则运算

    转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6412875.html 感谢博客:http://blog.csdn.net/itismelzp/article/ ...

  3. Java位运算实现加减乘除四则运算

    本文是继<一文了解有趣的位运算>的第二篇文章. 我们知道,计算机最基本的操作单元是字节(byte),一个字节由8个位(bit)组成,一个位只能存储一个0或1,其实也就是高低电平.无论多么复 ...

  4. 位运算实现加减乘除四则运算(Java)

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 本文是继< ...

  5. 剑指offer用位运算实现两个数相加,及python相关的位操作

    题目:写一个函数,求两个整数之和,要求在函数体内不得使用+.-.*./四则运算符号. 代码: # -*- coding:utf-8 -*-class Solution:    def Add(self ...

  6. php实现不用加减乘除号做加法(1、善于寻找资源:去搜为什么位运算可以实现加法,里面讲的肯定要详细一万倍)

    php实现不用加减乘除号做加法(1.善于寻找资源:去搜为什么位运算可以实现加法,里面讲的肯定要详细一万倍) 一.总结 1.善于寻找资源:去搜为什么位运算可以实现加法,里面讲的肯定要详细一万倍 二.ph ...

  7. Python笔记_第一篇_面向过程_第一部分_3.进制、位运算、编码

    通过对内存这一个部分的讲解,对编程会有一个相对深入的认识.数据结构是整个内存的一个重要内容,那么关于数据结构这方面的问题还需要对进制.位运算.编码这三个方面再进行阐述一下.前面说将的数据结构是从逻辑上 ...

  8. Java 位运算2-LeetCode 201 Bitwise AND of Numbers Range

    在Java位运算总结-leetcode题目博文中总结了Java提供的按位运算操作符,今天又碰到LeetCode中一道按位操作的题目 Given a range [m, n] where 0 <= ...

  9. 简简单单学会C#位运算

    一.理解位运算 要学会位运算,首先要清楚什么是位运算?程序中的所有内容在计算机内存中都是以二进制的形式储存的(即:0或1),位运算就是直接对在内存中的二进制数的每位进行运算操作 二.理解数字进制 上面 ...

随机推荐

  1. redis的缓存穿透 缓存并发 缓存失效

    我们在用缓存的时候,不管是Redis或者Memcached,基本上会通用遇到以下三个问题: 缓存穿透 缓存并发 缓存失效 一.缓存穿透 Paste_Image.png Paste_Image.png ...

  2. Nodejs文件相关操作

    欢迎关注我的博客我在马路边 适用人群 本文适用于刚接触Node的小白,毕竟我也是小白,大佬请绕行. Node文件操作 在实际开发中遇到很多有关文件及文件夹的操作,比如创建.删除文件及文件夹,文件拷贝. ...

  3. bzoj 1711 Dining吃饭 —— 最大流

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1711 食物一列,牛拆点,饮料一列. 代码如下: #include<cstdio> ...

  4. ORACLE数据库增加表空间大小或给表空间增加数据文件

    转载 2017年11月24日 11:00:28 ----查询表空间使用情况--- SELECT UPPER(F.TABLESPACE_NAME) "表空间名", D.TOT_GRO ...

  5. java中toString() 、(String)、String.valueOf的区别

    1.采用toString() 在使用时要注意,必须保证object不是null值,否则将抛出NullPointerException异常.采用这种方法时,通常派生类会覆盖Object里的toStrin ...

  6. 2006年清华:N的阶乘

    题目描述: 输入一个正整数N,输出N的阶乘. 输入: 正整数N(0<=N<=1000) 输出: 输入可能包括多组数据,对于每一组输入数据,输出N的阶乘 样例输入: 4 5 15 样例输出: ...

  7. Python:easygui的安装、导入、使用、设置

    转于:https://blog.csdn.net/sinat_37390744/article/details/55211652 博主:钏的博客 一.下载安装 1)下载0.96的easygui.htt ...

  8. arm交叉编译 扫盲贴

    ARM交叉编译工具链 为什么要用交叉编译器? 交叉编译通俗地讲就是在一种平台上编译出能运行在体系结构不同的另一种平台上的程序, 比如在PC平台(X86 CPU)上编译出能运行在以ARM为内核的CPU平 ...

  9. Centos7搭建pptp

    废话不多说,先上脚本地址:Centos7一键pptp 使用方法: wget https://raw.githubusercontent.com/DanylZhang/VPS/master/CentOS ...

  10. TS学习之类

    定义类 class Person { name: string; //属性 constructor(_name: string) { this.name = _name; } //构造函数 sayHe ...