mis-lab / Front-End-Question-Discussion

前端基础问题 每日一题
1 stars 0 forks source link

不准用加减乘除做加减乘除系列 | JavaScript的位操作做运算 #33

Open Reaper622 opened 4 years ago

Reaper622 commented 4 years ago

JavaScript 位运算基础操作

二进制的一些相关运算如下:

操作名 结果 结果 结果 结果
与(&) 0&0 == 0 0 & 1 == 0 1&0 == 0 1&1 == 1
或(|) 0|0 == 0 0|1 == 1 1|0 == 1 1|1 == 1
异或(^) 0^0 == 0 0^1 == 1 1^0 == 1 1^1==0
取反 1变0 0变1 最高位为1则为负数  

位的移动运算: <<为左移运算符, m<<n表示把m左移动n位。左移n位时,最左边的n位会丢弃,最右侧会补上n个0

>>为右移运算符,m>>n表示把m右移动n位,右移动n位时,最右侧的n位会被丢弃,但是左侧会根据正负而产生不同的结果,当为正数时,左侧补上n个0,当为负数时,左侧补上n个1。

>>>为无符号右移运算符,此时右移不考虑正负因素,全部用0填充。

不使用加减乘除操作符实现加减乘除法

位运算实现加法

位运算实现加法。异或之后通过与运算获取进位进位一位后递归求和,直到无进位。

/**
 * 位运算实现加法。异或之后通过与运算获取进位进位一位后递归求和,直到无进位。
 * @param {number} num1 
 * @param {number} num2 
 * @return {number}
 */
function plus (num1: number, num2: number): number {
    if (num2 === 0) {
        return num1;
    }
    // 取两者的 异或 即和 (不含进位)
    let sum = num1 ^ num2;
    // 取二者的进位 向左移动一位
    let carry = (num1 & num2) << 1;
    return plus (sum, carry);
}

位运算实现减法

计算机内,减法被转化为加上该数的负数,即我们也以加法实现。

/**
 * 计算机内,减法被转化为加上该数的负数,即我们也以加法实现。
 * @param {number} num1 
 * @param {number} num2 
 * @return {number}
 */
function minus(num1: number, num2: number): number {
    // 我们通过求补码算出该数的负数, 取反 + 1 即为 补码
    let subtractor = plus(~num2, 1);
    // num1 + num2补码 即为 num1 - num2
    return plus(num1, subtractor);
}

位运算实现乘法

在已经实现加法的情况下,乘法直接为我们被乘数 加上乘数倍的自己即可,但要考虑负数的情况。

我们可以先通过计算绝对值的乘数结果,之后根据符号位结果确定最终符号即可。

/**
 * 在已经实现加法的情况下,乘法直接为我们被乘数 加上乘数倍的自己即可,但要考虑负数的情况。
 * 我们可以先通过计算绝对值的乘数结果,之后根据符号位结果确定最终符号即可。
 * @param {number} num1 
 * @param {number} num2 
 * @return {number}
 */
function multiply(num1: number, num2: number): number {
    // 取绝对值
    let num1Abs = num1 < 0 ? plus(~num1, 1) : num1;
    let num2Abs = num2 < 0 ? plus(~num2, 1) : num2;
    // 计算绝对值的乘积
    // 记录循环次数
    let count = 0;
    // 最终结果
    let result = 0;
    while (count < num2Abs) {
        result = plus(result, num1Abs);
        count = plus(count, 1);
    }
    // 确定最终结果的符号
    if ((num1 ^ num2) < 0) {
        result = plus(~result, 1);
    }
    return result;
}

位运算实现除法

除法运算即可以看做被除数一直减去除数直到被除数小于除数为止,此时进行减法的次数就是商,而剩余的被除数就是余数。

同样,除法也要注意符号问题,我们和乘法对于符号的解决方法相同。

/**
 * 除法运算即可以看做被除数一直减去除数直到被除数小于除数为止,此时进行减法的次数就是商,而剩余的被除数就是余数。
 * 同样,除法也要注意符号问题,我们和乘法对于符号的解决方法相同。
 * @param {number} num1 
 * @param {number} num2 
 * @return {number}
 */
function divide(num1: number, num2: number): number {
    //先求被除数和除数的绝对值
    let num1abs = num1 < 0 ? plus(~num1, 1) : num1;
    let num2abs = num2 < 0 ? plus(~num2, 1) : num2;
    // 记录循环次数
    let count = 0;
    let result = num1abs;
    while (result >= num2abs) {
        result = minus(result, num2abs);
        count = plus(count, 1);
    }
    // 循环次数即为商
    return count;
}