给定两个数,用位运算实现加减乘除(不适用加减乘除号)

package com.demo.algorithm.bitmap位图;

/**
 * 用位运算实现 加减乘除
 */
public class Code02_BitAddSubtractMultDiv {

    /**
     * 加法
     * ^  异或运算(都为1时 结果为0,且没有进位) 是 无符号相加 例如 0011^1010 = 0010
     *  (a & b) << 1 得到 a + b 的 进位信息
     *
     *  a = a^b , b = (a & b) << 1 一直循环 到 进位信息没有 即 为 0
     * @param a
     * @param b
     * @return
     */
    public static int add(int a, int b){
        int sum = a;
        while (b != 0){
            sum = a ^ b;
            b = (a & b) << 1;
            a = sum;
        }
        return sum;
    }

    /**
     * 减法
     * a + b = a +(-b)
     * -b = ~b +1 ;
     * 正数的反码与其原码相同;负数的反码是对正数逐位取反,符号位保持为1
     * 假如 b = 10 = 0000 1010;
     * 对 b 取反 得到 ~b = 0000 0101;
     * 取反后加一 得到 ~b +1 = 0000 0110;
     * @param a
     * @param b
     * @return
     */
    public static int subtract(int a, int b){
        return add(a,negNum(b));
    }

    /**
     * 求相反数 取反加一
     * @param n
     * @return
     */
    public static int negNum(int n){
        return add(~n,1);
    }

    /**
     * 乘法
     * 1. 从左到右,依次 看 b 的每一位上的数 是否为1(看b的 哪些位置是1, 即 把 b 右移i 位,和 1 相与,若结果 为1 则说明 b的i位置 是1 ) ,
     * 2. 如果i位置 是 1 则 把 a 左移i位,空位补0, 得到的结果 相加
     * 3. 重复 上面步骤,循环 b 的 长度次。
     * 例如 a = 10101, b = 101, 则 res = a*(2^0 + 2^2) = a*2^0 + a*2^2
     * 而 a*2^n 等价于 把 a 左移n 位 ,
     * 所以 a*b 的结果 就是 把 a左移0位 + 把 a 左移 2 位
     * @param a
     * @param b
     * @return
     */
    public static int multi(int a, int b){
        int res = 0;
        while (b != 0 ){ //这里 b!=0 而不是 b >0 ,因为b可能为负数
            if((b & 1) != 0){
                res += a;
            }
            a <<= 1;
            b >>>= 1;//不带符号右移, 如果带符号右移的话,当 b 为 负数时,则会 循环移动 31 次, 但是实际结果 应该时 b 不带符号 循环右移 b的 实际长度次
        }
        return res;
    }

    /**
     * 除法 有四种情况(由于整型最小数是没有绝对值的(最小数的 绝对值 = 最大数 +1 ,无法用整型数表示),所以不能直接用 div 方法)
     * 1. 除数是整型最小值,被除数也是整型最小值 结果为1
     * 2. 除数是最小值,被除数不是最小值
     * 3. 除数不是最小值,被除数是最小值 结果为 0
     * 4. 除数 和 被除数 都不是最小值
     * @param dividend
     * @param divisor
     * @return
     */
    public int divide(int dividend, int divisor) {
        if (dividend == Integer.MIN_VALUE && divisor == Integer.MIN_VALUE){
            return 1;
        }else if (dividend == Integer.MIN_VALUE ){
            if (divisor == -1){
                return Integer.MAX_VALUE;
            }else {
                //由于最小数不能直接取绝对值,会越界,故 a/b 之间 a +1 再取绝对值
                //step1. (a+1)/b = c;
                //step2. a - c*b = d;
                //step3. d /b = e;
                //step4. a/b = c+e = (a+1)/b + (a-c*b)/b;
                int c = div((dividend + 1),divisor);
                int d = dividend - multi(c, divisor);
                return c + div(d,divisor);
            }

        }else if (divisor == Integer.MIN_VALUE){
            return 0;
        }else {
            return div(dividend, divisor);
        }
    }

    /**
     * 除法:
     * 除数和被除数取绝对值后 再进行运算 , 得到的结果 再加上 符号(同符号为正,相反为负)
     * 1. 被除数从 30 开始递减 移动 i位
     * 2. 当 移动到i位时, 除数 最接近 被除数,且不小于被除数 时, 得知 商的组成部分中,在 i 位置肯定有一个1;
     * 3. 用原来的数据减去 上一步移动后的 数,得到的结果 再重复 1,2 ,3 步
     * @param x
     * @param y
     * @return
     */
    public int div(int x, int y){
        int res = 0;
        int a = x < 0 ? -x : x;
        int b = y < 0 ? -y : y;
        //因为 第31 位 是符号位,故只需从 第30 位移起
        for (int i = 30; i >= 0; i--) {
            while ((a >> i) >= b){ //当 移动到i位时, 除数 最接近 被除数,且不小于被除数 时
                res |= (1<<i); //商 在 i 位置肯定有1
                a = a - (b << i); //用原来的数据减去 上一步移动后的 数,得到的结果 再重复
            }
        }
        //异或:无符号相加(没有进位信息) 同0异1,即 运算两边相同则为0即false,两边不同则为1即true;
        return ((x < 0) ^ (y < 0)) ? -res : res;
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值