1.位运算实现加、减、乘、除运算。

1.位运算:

  • 1-1.计算机最基本的操作单元是字节(byte),一个字节由8个位(bit)组成,一个位只能存储一个0或1,其实也就是高低电平。无论多么复杂的逻辑、庞大的数据、酷炫的界面,最终体现在计算机最底层都只是对0101的存储和运算。因此,了解位运算有助于提升我们对计算机底层操作原理的理解。

2.位运算实现加法运算:

  • 2.1.十进制的加法运算:13 + 9 = 22。拆分运算过程:
    • ①不考虑进位,分别对各位数进行相加,结果为 sum(1):个位数 3 加上 9 为 2,十位数 1 加上 0 为 1;最终结果为 12;
    • ②只考虑进位,结果为 carry(1):3 + 9 有进位,进位的值为 10;
    • ③如果步骤 ② 所得进位结果 carry 不为 0,对步骤 ① 所得 sum(1),步骤 ②所得 carry(1) 重复步骤 ①、②、③;如果 carry(n) 为 0 则结束,最终结果为步骤 ① 所得 sum(n) :
      • α:对 sum(1) = 12 和 carry(1) = 10 重复以上三个步骤:不考虑进位:sum(2) = 22;只考虑进位:carry(2) = 0;carry(2) = 0,结束;sum(2) = 22。
  • 2.2.二进制的加法运算:13 的二进制为 0000 1101,9 的二进制为 0000 1001:00001101 + 00001001 = 0001 0110;拆分运算过程:
    • ①不考虑进位:sum(1) = 0000 1101 + 0000 1001 = 0000 0100;
    • ②考虑进位:有两处进位,第 0 位和第 3 位,只考虑进位的结果为:carry(1) = 0001 0010;
    • ③步骤 ② carry(1) 不为 0,重复步骤 ①、②、③;carry(n) 为 0 则结束,结果为 sum(n):
      • α:不考虑进位 sum(2) = 0001 0110;只考虑进位:carry(2) = 0;carry(2) = 0,结束,最终结果为 sum(2) = 0001 0110。
  • 2.3.第一步不考虑进位的加法其实就是异或运算;第二步只考虑进位就是 与运算并左移一位;第三步就是重复前面两步操作直到第二步进位结果为 0。
  • 2.4.代码实现:计算机其实就是通过位运算实现加法运算的(通过加法器,加法器就是使用下面方法实现加法的)。
    • ①递归:
function add(a, b) {
  if(b === 0) {
    return a;
  }
  let sum = a ^ b;
  let carry = (a & b) << 1;
  return add(sum, carry);
}
add(1284, 18898); // 20182
  • ②迭代:
function addSum(a, b) {
  let sum = a ^ b;
  let carry = (a & b) << 1;
  while(carry !== 0) {
    let a = sum;
    let b = carry;
    sum = a ^ b;
    carry = (a & b) << 1;
  }
  return sum;
}
addSum(1284, 189998) // 191282

3.位运算实现减法运算:减法运算变形为一个正数加上一个负数:

  • 3.1.将数字的正负号变号的方式叫补码,求取步骤:总结为取反加 1:
    • ①将一个二进制位都取相反值,0 变 1,1 变 0(即反码)。
    • ②将上一步得到的值(反码)加 1。
  • 3.2.代码实现:
function add(a, b) {
  if(b === 0) {
    return a;
  }
  let sum = a ^ b;
  let carry = (a & b) << 1;
  return add(sum, carry);
}

function substract(a, b) {
  let unB = add(~b, 1); // 先求减数的补码并加 1(取反加1)
  let result = add(a, unB); // 求和加法运算
  return result;  
}
substract(100, 99) // 1

4.位运算实现乘法运算:

  • 4-1.乘数和被乘数的绝对值的乘积,再根据它们的符号确定最终结果的符号即可:代码实现:规定每个字节的最高位为符号位。1为负。被乘数 * 乘数 = 积
  • 4-2.乘数与被乘数的绝对值的乘积使用累加计算。
function add(a, b) {
  if(b === 0) {
    return a;
  }
  let sum = a ^ b;
  let carry = (a & b) << 1;
  return add(sum, carry);
}

function multiply(a, b) {
  let multiplicand = a < 0 ? add(~a, 1) : a;
  let multiplier = b < 0 ? add(~b, 1) : b;
  let product = 0;
  let count = 0;
  while(count < multiplier) {
    product = add(product, multiplicand); // 累加
    count = add(count, 1); // 计数器
  };
  if((a ^ b) < 0) {
    product = add(~product, 1);
  } 
  return product;
}
multiply(98, 99); // 9702
  • 4-3.乘数与被乘数的绝对值使用手写方式计算。
function add(a, b) {
  if(b === 0) {
    return a;
  }
  let sum = a ^ b;
  let carry = (a & b) << 1;
  return add(sum, carry);
}

function multiply(a, b) {
  let multiplicand = a < 0 ? add(~a, 1) : a; // 被乘数
  let multiplier = b < 0 ? add(~b, 1) : b; // 乘数

  let product = 0;
  while(multiplier > 0) {
    if(multiplier & 1) {// 每次考察乘数的最后一位    
      product = add(product, multiplicand);     
    }     
    multiplicand = multiplicand << 1;// 每运算一次,被乘数要左移一位    
    multiplier = multiplier >> 1;// 每运算一次,乘数要右移一位(可对照上图理解) 
  };
  if((a ^ b) < 0) {
    product = add(~product, 1);
  }
  return product;
}
multiply(98, 99); // 9702

5.位运算实现除法:

  • 5-1.转换成减法运算,即不停的用除数去减被除数,直到被除数小于除数时,此时所减的次数就是我们需要的商。被除数÷除数=商
  • 5-2.性能慢:
function add(a, b) {
  if(b === 0) {
    return a;
  }
  let sum = a ^ b;
  let carry = (a & b) << 1;
  return add(sum, carry);
}

function substract(a, b) {
  let unB = add(~b, 1); // 先求减数的补码并加 1(取反加1)
  let result = add(a, unB); // 求和加法运算
  return result;  
}

function divide(a, b) {
  let dividend = a < 0 ? add(~a, 1) : a; // 被除数
  let divisor = b < 0 ? add(~b, 1) : b; // 除数
  let quotient = 0; // 商
  let remainder = 0; // 余数
  while(dividend >= divisor){// 当被除数小于除数时,即除不尽了。      
    quotient = add(quotient, 1); // 商,累加计数器       
    dividend = substract(dividend, divisor); // 被除数 - 除数    
  }
  if((a ^ b) < 0) {
    quotient = add(~quotient, 1);
  } 
  remainder = b > 0 ? dividend : add(~dividend, 1);
  return quotient;
}
  • 5-3.优化:int整型的表示范围是-2147483648~+2147483647, [2^0, 21,…,31]。采用类似二分法的思路,从除数*最大倍数开始测试,如果比被除数小,则要减去。下一回让除数的倍数减少为上一次倍数的一半,这样的直到倍数为1时,就能把被除数中所有的除数减去,并得到商。只有当a >= b时才可以上商,又因为是二进制,所以商每次只会多1,在每次上1之后a都要减去一次b。
function add(a, b) {
  if(b === 0) {
    return a;
  }
  let sum = a ^ b;
  let carry = (a & b) << 1;
  return add(sum, carry);
}

function substract(a, b) {
  let unB = add(~b, 1); // 先求减数的补码并加 1(取反加1)
  let result = add(a, unB); // 求和加法运算
  return result;  
}
function divide(a, b) {   
  let dividend = a < 0 ? add(~a, 1) : a; // 被除数
  let divisor = b < 0 ? add(~b, 1) : b; // 除数
  let quotient = 0; // 商
  let remainder = 0; // 余数
  for(let i = 31; i >= 0; i--) {
    //比较dividend是否大于divisor的(1<<i)次方,不要将dividend与(divisor<<i)比较,而是用(dividend>>i)与divisor比较,
    //效果一样,但是可以避免因(divisor<<i)操作可能导致的溢出,如果溢出则会可能dividend本身小于divisor,但是溢出导致dividend大于divisor       
    if((dividend >> i) >= divisor) { // dividend >= (dividend << i) 更容易理解
      console.log('被除数大于除数未处理',i, dividend,divisor,quotient)  
      quotient = add(quotient, 1 << i);            
      dividend = substract(dividend, divisor << i);     
      console.log('被除数大于除数',i, dividend,divisor,quotient)    
    }
    console.log('被除数小于除数',i, dividend,divisor,quotient)    
  }    
  if((a ^ b) < 0) {
    quotient = add(~quotient, 1);
  }
  remainder = b > 0 ? dividend : add(~dividend, 1);
  return quotient;
}
divide(9, 3)
被除数小于除数 31 9 3 0
被除数小于除数 30 9 3 0
被除数小于除数 29 9 3 0
被除数小于除数 28 9 3 0
被除数小于除数 27 9 3 0
被除数小于除数 26 9 3 0
被除数小于除数 25 9 3 0
被除数小于除数 24 9 3 0
被除数小于除数 23 9 3 0
被除数小于除数 22 9 3 0
被除数小于除数 21 9 3 0
被除数小于除数 20 9 3 0
被除数小于除数 19 9 3 0
被除数小于除数 18 9 3 0
被除数小于除数 17 9 3 0
被除数小于除数 16 9 3 0
被除数小于除数 15 9 3 0
被除数小于除数 14 9 3 0
被除数小于除数 13 9 3 0
被除数小于除数 12 9 3 0
被除数小于除数 11 9 3 0
被除数小于除数 10 9 3 0
被除数小于除数 9 9 3 0
被除数小于除数 8 9 3 0
被除数小于除数 7 9 3 0
被除数小于除数 6 9 3 0
被除数小于除数 5 9 3 0
被除数小于除数 4 9 3 0
被除数小于除数 3 9 3 0
被除数小于除数 2 9 3 0
被除数大于除数未处理 1 9 3 0
被除数大于除数 1 3 3 2
被除数小于除数 1 3 3 2
被除数大于除数未处理 0 3 3 2
被除数大于除数 0 0 3 3
被除数小于除数 0 0 3 3
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值