【趣味题目】不用加减乘除做加减乘除运算

不用加减乘除做加减乘除运算

预备知识

这类题目我选择使用位运算,需要了解到:两个数异或(a^b)结果是忽略进位的结果;两个数相与(a&b)<<1结果是产生的进位结果

对于一个有符号数,其相反数等于它的补码按位取反再加1。以一个8位有符号数-7为例,其二进制表示为 10000111(其中最高位为符号位,0表示正数,1表示负数),将其转换为补码需要将其反码再加1,即:

反码: 1111 1000
补码: 1111 1001
因此,-7的补码为 11111001。
如果要求-7的相反数,则需要按位取反,即得到其反码为 0000 0110,补码为 0000 0111,其十进制表示为7。因此,-7的相反数为7。

在计算机中,求一个数的相反数一般使用取反操作(~)和加法操作。具体操作如下:
int num = -7;
int oppositeNum = ~num + 1;

做加法

测试链接
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。

示例: 输入: a = 1, b = 1
输出: 2
示例: 输入: a = 1, b = -2
输出: -1

在 C++的实现中,当我们赋给带符号类型一个超出它表示范围的值时,结果是 undefined;而当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模的余数。因此,我们可以使用无符号类型来防止溢出。

class Solution {
public:
    int add(int a, int b) {
    	//只能正整数相加
        // int carry=(a&b)<<1;
        // int num=a^b;
        // if(carry) num=add(num,carry);

        // return num;
		
		//整数和负数都可以
        while (b)
        {
            size_t carry = (size_t)(a & b) << 1;
            int num = a ^ b;
            a = num;
            b = carry;
        }
	    return a;
    }
};

做减法

  1. 减法运算可以直接用上面的加法。

  2. 自定义一个减法运算
    ~a 表示 a 的逐位取反操作,即 ~a = -a - 1。因此,carry 即为 a 和 b 的需要进位的位置。
    最后,将 carry 左移一位作为进位,然后重复执行上述操作,直到 b 等于 0,即不再有进位为止。最终返回的结果即为不使用加减乘除实现的 a-b。


int subtract(int a, int b) {
	int carry ;
	while (b != 0) {
		carry = (~a) & b; // 计算a和b的不进位相减结果
		a ^= b; // 计算a和b的不考虑进位的加法结果
		b = carry << 1; // b左移一位作为进位
		
	}
	return a;
	
}
  1. 或者是第一步对减数取反然后加1,第二步将第一步所得值和被减数相加
int subtract(int a, int b)
{
	int subtrahend = add(~b, 1);//得到相反数

	int sub = add(a, subtrahend);

	return sub;
}

做乘法

乘法的本质就是加法
例如:1113—>10111101=10001111
首先判断乘数的第0位,这里第0位为1,因此第一个相加数为1101。然后判断乘数的第一位,这里第一位为0,所以第二个相加数为00000,即被乘数左移一位。继续看乘数的第二位,这里第二位为1,则第三个相加数为101100。最后看乘数的第四位,这里为1,所以最后一个相加数为1101000即被乘数左移三位。最后将获得的所有相加数都加起来,而这个和就是所要求的乘积(1101+11010+0+1101000=10001111)

根据二进制位可以想到,乘法中将被乘数乘以2同时乘数除以2,结果不变!!!

1011*1101==1011*1 + 10110*0 + 101100*1 + 1011000*1
int multiply(int a, int b)
{
	//将乘数和被乘数都取绝对值
	int left = a < 0 ? add(~a, 1) : a;
	int right = b < 0 ? add(~b, 1) : b;

	//计算绝对值的乘积
	int product = 0;
	while (right)
	{
		if (right & 1)
		{
			product = add(left, product);
		}

		//被乘数左移一位
		left = left << 1;
		//乘数右移一位
		right = right >> 1;
	}

	//计算乘积的符号
	if ((a ^ b) < 0)
	{
		product = add(~product, 1);
	}

	return product;
}

做除法|取模

除法实现就是不停的用除数去减被除数,直到被除数小于除数时,此时所减的次数就是我们需要的商,而此时的被除数就是余数。需要注意的是商的符号和余数的符号。商的符号确定方式也乘法是一样,即同号为正,异号为负。而余数的符号和被除数的符号是一样的。和简单的乘法实现一样,这里我们要先对两数的绝对值求商,求余数。最后再确定符号。具体实现代码如下:

//求商
int divide(int a, int b)
{
  //对被除数和除数取绝对值
  int dividend = a < 0 ? add(~a, 1) : a;
  int divisor = b < 0 ? add(~b, 1) : b;
 
  //对被除数和除数的绝对值求商
  int remainder = dividend;
  int quotient = 0;//计数器(商)
 
  while(remainder >= divisor)
  {
    remainder = subtract(remainder, divisor);//不断相减
    quotient = add(quotient, 1);
  }
 
  //求商的符号
  if((a ^ b) < 0)
  {
    quotient = add(~quotient, 1);
  }
 
  return quotient;
}

取模

//取模
int remainder(int a, int b)
{
  //对被除数和除数取绝对值
  int dividend = a < 0 ? add(~a, 1) : a;
  int divisor = b < 0 ? add(~b, 1) : b;
 
  int remainder = dividend;
 
  while(remainder >= divisor)
  {
    remainder = subtract(remainder, divisor);
  }
 
  //取模的符号
  if(a < 0)
  {
    remainder = add(~remainder, 1);
  }
 
  return remainder;
}

本文系个人见解,如有不妥,敬请指出

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值