实现二进制加法器

521 阅读6分钟

二进制存储

在计算机中,数据是用二进制0、1来存储的,第一位用来表示符号位,当第一位是1时,则为负数,当第一位是0是,为整数。

八位的二进制中,除了首位的符号位,还剩七位可以表示数字,所以八位二进制能表示的范围为 1111 1111 -> 0111 1111 即为 -127 -> 127 ,这255个数字

比如在八位的二进制数中

3 在二进制中表示为 0000 0011

-3 在二进制中表示为 1000 0011

二进制加法

在计算二进制加法的时候,也是按照十进制的方式,只不过十进制是逢十进一,二进制十逢二进一

0000 1001 + 0000 0001 = 0000 1010 -> 9 + 1 = 10

上面的计算过程是:

  1. 右边第一位:1 + 1 = 2向左进一位,减2剩0,当前位置写0
  2. 右边第二位:0 + 0 + 上面的进位1 = 1当前位置写1
  3. 右边第三位:0 + 0 = 0当前位置写0
  4. 右边第四位:0 + 1 = 1当前位置写1
  5. 再左边的每项相加的都是0,则结果为0000 1010

0000 0011 + 0000 0011 = 0000 0110 -> 3 + 3 + 6 上面的计算过程是:

  1. 右边第一位:1 + 1 = 2向左进一位,减2剩0,当前位置写0
  2. 右边第二位:1 + 1 + 上面的进位1 = 3向左进一位,减2剩1,当前位置写1
  3. 右边第三位:0 + 0 + 上面的进位1 = 1当前位置写1
  4. 再左边的每项相加的都是0,则结果为0000 0110

原码

将一个整数转换成二进制形式,就是其原码。例如5的原码就是0000 0101更改 a 的值;-19的原码是1001 0011,通俗的理解,原码就是一个整数本来的二进制形式。

反码

正数与负数的反码不一样。 对于正数,它的反码就是其原码(原码和反码相同); 负数的反码是将原码中除符号位以外的所有位(数值位)取反,也就是 0 变成 11 变成 0。例如 5原码和反码都是 0000 0101, -5反码是 0111 1010

补码

如果按照上面的方式计算减法就会出现问题,比如 -1 + 2 1000 0001 + 0000 0010 = 1000 0011 最终的结果是-3

对于0-0的存储如果按照上面的方式就是0000 00001000 0000,这样存储的话,0这个数字在计算机中的编码就不是唯一的了。

为了解决上面的两个问题,就引入了补码,采用补码成功解决了数字 0 在计算机中非唯一编码的问题,也实现了减法变加法

正数的补码等于源码等于反码,负数的补码等于负数的反码+1

对于有符号数,内存要区分符号位和数值位,要是能把符号位和数值位等同起来,让它们一起参与运算,不再加以区分,只用加法器就可以同时实现加法和减法运算,这样硬件电路就变得简单了。 8 - 3 等价于 8 + (-3)

二进制减法计算

计算-7 + 2

  1. 将十进制转化为二进制
    • -71000 0111
    • 30000 0011
  2. 将原码转化为补码
    • 1000 0111 反码 1111 1000 补码 1111 1001
    • 0000 0011 反码 0000 0011 补码 0000 0011
  3. 补码相加 1111 1001 + 0000 0011 = 1111 1100
  4. 可以看出补码相加结果为负数,要想得到我们想要的结果,需要将补码转化为原码(补码减一再取反)
  5. 1111 1100 - 0000 0001 = 1111 1011 除了符号位取反结果为 1000 0100 十进制表示为 -4

计算4 + (-2)

  1. 将十进制转化为二进制
    • -21000 0010
    • 40000 0100
  2. 将原码转化为补码
    • 1000 0010 反码 1111 1101 补码 1111 1110
    • 0000 0100 反码 0000 0100 补码 0000 0100
  3. 补码相加 1111 1110 + 0000 0100 = 0000 0010(溢出忽略)
  4. 可以看出补码相加结果为正数,正数的原码和补码相同,则十进制表示为2

利用js实现一个加法器

/**
 * 二进制相加
 * @param {string} num1 二进制字符串
 * @param {string} num2 二进制字符串
 * @returns 二进制字符串之和
 */
function sum(num1, num2) {
    if (num1.length < num2.length) {
        return sum(num2, num1);
    }
    num2 = num2.padStart(num1.length, '0')
    let i = num1.length - 1;
    let strArr = [];
    let carry = 0;
    while (i >= 0) {
        let n1 = +num1.at(i);
        let n2 = +num2.at(i);
        let curSum = n1 + n2 + carry;
        if (curSum < 2) {
            strArr.unshift(curSum);
            carry = 0;
        }
        else if (curSum === 2) {
            strArr.unshift(0)
            carry = 1;
        } else if (curSum === 3) {
            strArr.unshift(1)
            carry = 1;
        }
        i--;
    }
    return strArr.slice(0, num1.length).join('');
}

/**
 * 按位取反
 * @param {string} str 二进制字符串
 * @returns {string} 取反后的二进制字符串
 */
function BitwiseInversion(str) {
    return str.split('')
        .map(item => item === '0' ? '1' : '0')
        .join('');
}

/**
 * 原码转补码|补码转源码(除了符号位按位取反 + 1)
 * @param {string} source 原码字符串
 * @returns {string} 补码字符串
 */
function conversion(source) {
    return sum('1' + BitwiseInversion(source.slice(1)), '1')
}

/**
 * 二进制转十进制 
 * @param {string} binaryStr 二进制字符串
 * @returns {string} 十进制字符串
 */
function binaryToDecimal(binaryStr) {
    let symbol = binaryStr.at(0);
    decimal = parseInt('' + binaryStr.slice(1), 2)
    if (symbol === '1') {
        decimal = '-' + decimal
    }
    return decimal;
}

/**
 * 计算-127到127以内的二进制加减法
 * 也就是 1111,1111 到 0111,1111
 * @param {string} num1 二进制字符串
 * @param {string} num2 二进制字符串
 * @returns {string} 两个二进制字符串之和的十进制字符串
 */
function adder(num1, num2) {
    if (num1.startsWith(1)) {
        num1 = conversion(num1);
    }
    if (num2.startsWith(1)) {
        num2 = conversion(num2);
    }
    let complement = sum(num1, num2);

    // 如果是负数,将补码转化为原码
    if (complement.startsWith('1')) {
        complement = conversion(complement.slice(1));
    }

    return binaryToDecimal(complement);
}

// 测试
console.log(adder('10000111', '00000010'));// -7 + 2
console.log(adder('10000111', '10000010'));// -7 + -2
console.log(adder('00000111', '10000010'));//  7 + -2
console.log(adder('00000111', '00000010'));//  7 + 2
console.log(adder('10000000', '00000010'));//  -0 + 2
console.log(adder('01111110', '00000001'));//  126 + 1