【问题标题】:Javascript: Add two binary numbers (returning binary)Javascript:添加两个二进制数(返回二进制)
【发布时间】:2017-03-14 04:11:48
【问题描述】:

我有两个二进制输入,我也以二进制返回加法结果。

var addBinary = function(a, b) {
    var dec = Number(parseInt(a, 2)) + Number(parseInt(b, 2));
    return dec.toString(2);
};

对于一些像

这样非常大的二进制文件

a = 10100000100100110110010000010101111011011001101110111111111101000000101111001110001111100001101

b = 110101001011101110001111100110001010100001101011101010000011011011001011101111001100000011011110011

我正在输出

110111101100010011000101110110100000011101000101011000000000000000000000000000000000000000000000000

假定正确的输出在哪里

110111101100010011000101110110100000011101000101011001000011011000001100011110011010010011000000000

是因为溢出吗?如果是这样,Javascript中二进制加法溢出的限制是什么?对不起,一堆 1 和 0。

【问题讨论】:

标签: javascript binary numbers


【解决方案1】:

我已经开发了一种在 Javascript 中进行二进制加法的解决方案。

我最初的目标是通过复制 Javascript 中数字二进制加法器电路中使用的机制来巩固我对二进制逻辑的理解(不使用基本转换或位运算符)。

您可以在 CodePen 上找到我的原始项目的工作版本。

它对 DOM 所做的工作比您可能需要的要多得多,但是当我插入您的数字时(通过下面提到的调整),我很高兴看到它起作用了!

Working Solution Code

此解决方案假定ab 是长度相同的字符串。要使用此解决方案,您的输入变量应修改为:

var a = "000010100000100100110110010000010101111011011001101110111111111101000000101111001110001111100001101"

var b = "110101001011101110001111100110001010100001101011101010000011011011001011101111001100000011011110011"

(我只是用零填充了var a前面缺少的数字。)

如您所见,我重新创建了二进制加法器电路的物理实现中使用的所有组件:

半加法器:

function halfAdder(a, b){
  const sum = xor(a,b);
  const carry = and(a,b);
  return [sum, carry];
}

全加器:

function fullAdder(a, b, carry){
  halfAdd = halfAdder(a,b);
  const sum = xor(carry, halfAdd[0]);
  carry = and(carry, halfAdd[0]);
  carry = or(carry, halfAdd[1]);
  return [sum, carry];
}

逻辑门:

function xor(a, b){return (a === b ? 0 : 1);}
function and(a, b){return a == 1 && b == 1 ? 1 : 0;}
function or(a, b){return (a || b);}

主要功能:

function addBinary(a, b){

  let sum = '';
  let carry = '';

  for(var i = a.length-1;i>=0; i--){
    if(i == a.length-1){
      //half add the first pair
      const halfAdd1 = halfAdder(a[i],b[i]);
      sum = halfAdd1[0]+sum;
      carry = halfAdd1[1];
    }else{
      //full add the rest
      const fullAdd = fullAdder(a[i],b[i],carry);
      sum = fullAdd[0]+sum;
      carry = fullAdd[1];
    }
  }

  return carry ? carry + sum : sum;
}

那么,addBinary(a,b) 会产生正确的答案!

var a = "000010100000100100110110010000010101111011011001101110111111111101000000101111001110001111100001101"
var b = "110101001011101110001111100110001010100001101011101010000011011011001011101111001100000011011110011"
var answer = "110111101100010011000101110110100000011101000101011001000011011000001100011110011010010011000000000";

console.log(addBinary(a, b) == answer); //true

我希望我在这里所做的一些事情也对你有用!

【讨论】:

【解决方案2】:

这是我的看法:

逻辑很简单,就像小学教的那样,从最右边的数字开始:我将第一个数字的最后一位数字和第二个数字的最后一位数字相加,并保留下一轮的进位。

在每一轮中(在while 内)我都右切两个数字,例如:

// number
1101 -> 110
// The math is simple: 1101/10|0 (divide by 10 and convert to integer)

输入和输出是字符串,以克服JS maximum integer 的限制,其中字符串的长度可以大得多。

完整代码:

function binaryAddition(a,b){
  var result = "",
      carry = 0

  while(a || b || carry){
    let sum = +a.slice(-1) + +b.slice(-1) + carry // get last digit from each number and sum 

    if( sum > 1 ){  
      result = sum%2 + result
      carry = 1
    }
    else{
      result = sum + result
      carry = 0
    }
    
    // trim last digit (110 -> 11)
    a = a.slice(0, -1)
    b = b.slice(0, -1)
  }
  
  return result
}

// Tests
[
  ["0","0"],
  ["1","1"],
  ["1","0"],
  ["0","1"],
  ["10","1"],
  ["11","1"],
  ["10","10"],
  ["111","111"],
  ["1010","11"]
].forEach(numbers => 
   document.write(
     numbers[0] + " + " + 
     numbers[1] + " = " + 
     binaryAddition(numbers[0], numbers[1]) + 
     "      <mark> (" +
     parseInt(numbers[0], 2) + " + " + 
     parseInt(numbers[1], 2) + " = " + 
     parseInt(binaryAddition(numbers[0], numbers[1]),2) +
     ")</mark><br>" 
   )
)
document.body.style="font:16px monospace";

【讨论】:

  • 你能解释一下a.slice(-1)前面的加号在这里做什么吗?
  • @DanielWilliams - 这是将 String 转换为 Number (整数)的“技巧”。如果在之前字符串应用数学运算,javascript会将表达式计算为数字
【解决方案3】:

忘记 Javascript 的运算精度,想想如何在数学中加一个二进制。

例如,11 + 10

首先,我们应该从右到左开始。 现在我们得到 1 + 0 = 1 之后,我们进入下一个。 1 + 1 = 10 如果我们使用Javascript,如何得到结果。

我们知道,Mod 可以得到余数,Division 可以得到进位。 在十进制中,我们得到1 + 1 = 2,如何将2 转换为10。 我们可以使用

result % 2   // we can get single digit
result / 2 | 0   // we can get tens digit, `| 0` can remove decimal.

现在我们可以将两个字符串连接在一起。

BinaryNumber = result / 2 | 0 + result % 2 + ''  // string concat

所以我们的最终代码可以是这样的:

/**
 * @param {string} a
 * @param {string} b
 * @return {string}
 */
var addBinary = function(a, b) {
    var i = a.length - 1;
    var j = b.length - 1;
    var carry = 0;
    var result = "";
    while(i >= 0 || j >= 0) {
        var m = i < 0 ? 0 : a[i] | 0;
        var n = j < 0 ? 0 : b[j] | 0;
        carry += m + n; // sum of two digits
        result = carry % 2 + result; // string concat
        carry = carry / 2 | 0; // remove decimals,  1 / 2 = 0.5, only get 0
        i--;
        j--;
    }
    if(carry !== 0) {
        result = carry + result;
    }
    return result;
};

【讨论】:

    【解决方案4】:

    在我看来,这是因为那些数字太大而失去精度。

    var addBinary = function(a, b) {
        var dec = Number(parseInt(a, 2)) + Number(parseInt(b, 2));
        console.log("the number a is " + parseInt(a, 2));
        console.log("the number b is " + parseInt(b, 2));
        console.log("the dec is  " + dec);
        return dec.toString(2);
    };
    var a = "10100000100100110110010000010101111011011001101110111111111101000000101111001110001111100001101"
    var b = "110101001011101110001111100110001010100001101011101010000011011011001011101111001100000011011110011"
    console.log(addBinary(a, b));
    

    结果是

    the number a is 2.484789315402498e+28
    the number b is 5.2670055459872975e+29
    the dec is  5.515484477527547e+29
    110111101100010011000101110110100000011101000101011000000000000000000000000000000000000000000000000
    

    你可以看到numa和numb都损失精度。如果将最后一个结果转换为二进制:

     parseInt("110111101100010011000101110110100000011101000101011000000000000000000000000000000000000000000000000", 2)
    

    然后你得到:

     5.515484477527547e+29. 
    

    所以“toString(2)”的过程是对的。

    我们手动模拟二进制的过程来解决这个问题(假设输入的字符串是正确的,所以我的代码中没有捕获任何异常。运行环境是nodejs v4.6.0):

    "use strict"
    let addBinarybyChainhelen = function(a, b) {
        let alen = a.length;
        let blen = b.length;
    
        let i = alen - 1;
        let j = blen - 1;
    
        let result  = "";
        let carry   = 0;
    
        while(i >= 0 && j >= 0) {
            let acur = parseInt(a[i], 10);
            let bcur = parseInt(b[j], 10);
    
            let rcur = (acur + bcur + carry) % 2;
            carry = parseInt((acur + bcur + carry) / 2); 
    
            result += rcur;
    
            i--;
            j--;
        }
    
        while(i >= 0) {
            let acur = parseInt(a[i], 10);
            let rcur = (acur + carry) % 2;
            carry = parseInt((acur + carry) / 2); 
    
            result += rcur;
            i--;
        }
    
        while(j >= 0) {
            let bcur = parseInt(b[j], 10);
            let rcur = (bcur + carry) % 2;
            carry = parseInt((bcur + carry) / 2); 
    
            result += rcur;
            j--;
        }
    
        if(carry) {
            result += carry;
        }
    
        return result.split("").reverse().join("");
    }
    
    
    // use the function
    let a = "10100000100100110110010000010101111011011001101110111111111101000000101111001110001111100001101" 
    let b = "110101001011101110001111100110001010100001101011101010000011011011001011101111001100000011011110011"
    console.log(addBinarybyChainhelen(a, b))
    

    并获得正确的输出

    110111101100010011000101110110100000011101000101011001000011011000001100011110011010010011000000000
    

    【讨论】:

    • 这个答案必须被接受,简单且运作良好。
    【解决方案5】:

    支持不同长度二进制字符串的通用方案

    我在Cassandra Wilcox's nice answer 中添加了一个padZeroes() 函数来创建一个支持不同长度二进制字符串的通用解决方案?

    我已经针对leetcode 上的add binary problem 测试了这个解决方案,所以它应该非常强大。

    /**
     * @param {string} a
     * @param {string} b
     * @return {string}
     */
    
    // logic gates
    function xor(a, b) {
      return a === b ? 0 : 1;
    }
    
    function and(a, b) {
      return a == 1 && b == 1 ? 1 : 0;
    }
    
    function or(a, b) {
      return a || b;
    }
    
    function halfAdder(a, b) {
      const sum = xor(a, b);
      const carry = and(a, b);
      return [sum, carry];
    }
    
    function fullAdder(a, b, carry) {
      halfAdd = halfAdder(a, b);
      const sum = xor(carry, halfAdd[0]);
      carry = and(carry, halfAdd[0]);
      carry = or(carry, halfAdd[1]);
      return [sum, carry];
    }
    
    function padZeroes(a, b) {
      const lengthDifference = a.length - b.length;
      switch (lengthDifference) {
        case 0:
          break;
        default:
          const zeroes = Array.from(Array(Math.abs(lengthDifference)), () =>
            String(0)
          );
          if (lengthDifference > 0) {
            // if a is longer than b
            // then we pad b with zeroes
            b = `${zeroes.join('')}${b}`;
          } else {
            // if b is longer than a
            // then we pad a with zeroes
            a = `${zeroes.join('')}${a}`;
          }
      }
      return [a, b];
    }
    
    function addBinary(a, b) {
      let sum = '';
      let carry = '';
    
      const paddedInput = padZeroes(a, b);
      a = paddedInput[0];
      b = paddedInput[1];
    
      for (let i = a.length - 1; i >= 0; i--) {
        if (i == a.length - 1) {
          // half add the first pair
          const halfAdd1 = halfAdder(a[i], b[i]);
          sum = halfAdd1[0] + sum;
          carry = halfAdd1[1];
        } else {
          // full add the rest
          const fullAdd = fullAdder(a[i], b[i], carry);
          sum = fullAdd[0] + sum;
          carry = fullAdd[1];
        }
      }
      return carry ? carry + sum : sum;
    }
    

    【讨论】:

      【解决方案6】:

      受@thepatriot 的启发,我通过BigInt 对象制作了一个单行版本。

      var addBinary = (a, b) => {
        return (BigInt(`0b${a}`) + BigInt(`0b${b}`)).toString(2);
      };
      
      console.log(addBinary('10100000100100110110010000010101111011011001101110111111111101000000101111001110001111100001101','110101001011101110001111100110001010100001101011101010000011011011001011101111001100000011011110011'));

      【讨论】:

        【解决方案7】:

        我们可以将它们转换为十进制数并执行任何算术运算,然后再转换回二进制,而不是处理二进制。

        function calculateBinary(expr){
            let [pre,post]=expr.split(/[+|\-|\*|\/]/).map(binary=>parseInt(parseInt(binary, 2).toString(10).trim()));
            let sign=expr.match(/[+|\-|\*|\/]/)[0];
            let res=eval(pre+sign+post);
            return res.toString(2);
        }
        console.log(calculateBinary('11011+1000'))//=>100011
        console.log(calculateBinary('1000-0100'))//=>100
        console.log(calculateBinary('1000/0100'))//=>10
        console.log(calculateBinary('10*10'))//=>100

        【讨论】:

          【解决方案8】:

          只需转换为 int 并再次转换为 bin

          var addBinary = (a, b) => {
              var dec = Number(parseInt(a, 2)) + Number(parseInt(b, 2));
              return (dec >>> 0).toString(2);
          };
          
          console.log(addBinary(1000,11));

          【讨论】:

          • 这个答案将通过添加解释得到改善
          【解决方案9】:

          这是我的尝试……没有什么花哨的简单逻辑。

          var addBinary = function(a, b) {
            let aLast = a.length - 1;
            let bLast = b.length - 1;
          
            let carry = 0;
            let aStr = [];
          
            while(aLast>=0 || bLast>=0){
              let sum = carry;
              if(aLast >= 0) {
                sum+= Number(a[aLast]);
                aLast--;
              }
          
              if(bLast >= 0) {
                sum+= Number(b[bLast]);
                bLast--
              }
              aStr.push(sum%2);
              carry = Math.floor(sum/2);
            }
          
            if(carry)     aStr.push(carry);
          
            return aStr.reverse().join("");
          };
          

          【讨论】:

            【解决方案10】:

            我也想添加我的解决方案

            let a = '1111';
            let b = '1111';
            let addBinary = (a, b) => {
            
              let highestNumber;
              let lowestNumber;
              let answer = '';
              let carry = 0;
            
            	let aArr = a.split('');
              let bArr = b.split('');
              
              if(aArr.length > bArr.length) {
              
              	highestNumber = aArr;
                lowestNumber = bArr;
              } else {
              
              	highestNumber = bArr;
                lowestNumber = aArr;
              }
              
              let diff = highestNumber.length - lowestNumber.length;
              let startingInd = highestNumber.length - diff;
              
              
              for(let i= startingInd; i < highestNumber.length; i++) {
              
              	lowestNumber = ['0'].concat(lowestNumber);
                
              }
              
              
              for(let i=highestNumber.length-1; i >= 0; i--) {
                
                let num1 = parseInt(highestNumber[i]);
                let num2 = parseInt(lowestNumber[i]);
                let sum = num1 + num2 + carry;
                
                let currValue = sum === 1 || sum === 3 ? '1' : '0';
                
                answer = currValue.concat(answer);
                
                
                if(sum === 3 || sum === 2) carry = 1;
                
              }
              
              if(carry == 1) answer = '1'.concat(answer);
              if(carry == 2) answer = '10'.concat(answer);
              
              return answer;
              
            };
            
            
            console.log(addBinary(a, b));

            【讨论】:

              【解决方案11】:

              这是我的解决方案,使用几个条件语句。

              function addBinary(a, b) {
                let result = "";
                let i = a.length - 1;
                let j = b.length - 1;
                let carry = 0;
              
                while (i >= 0 || j >= 0 || carry > 0) {
                  const x = parseInt(a[i], 10) || 0;
                  const y = parseInt(b[j], 10) || 0;
                  const z = x + y + carry;
                  i--, j--;
              
                  // error handling: non-binary number
                  if (z > 3 || z < 0) return console.error("non-binary number");
              
                  result = z === 3 || z === 1 ? 1 + result : 0 + result;
                  carry = z < 2 ? 0 : 1;
                }
              
                return result;
              }
              

              条件语句的逻辑与使用以下 switch/case 的逻辑相同:

              switch (z) {
                 case 3:
                    result = 1 + result;
                    carry = 1;
                    continue;
                 case 2:
                    result = 0 + result;
                    carry = 1;
                    continue;
                 case 1:
                    result = 1 + result;
                    carry = 0;
                    continue;
                 case 0:
                    result = 0 + result;
                    carry = 0;
                    continue;
                 default:
                    return console.error("non-binary number");
              }
              

              【讨论】:

                【解决方案12】:

                我想提出 ES6 中最短的变体(主要要求是“避免使用内置的大整数来解决这个挑战”):

                // It's my challenge solution with 104 chars only:
                addBinaryStrings = ([...a], [...b]) => {
                    c = 0
                    r = ''
                    while (a[0] | b[0] | c) {
                        c += ~~a.pop() + ~~b.pop()
                        r = c%2 + r
                        c = c > 1
                    }
                    return r
                }
                
                // Test:
                binaryString1 = "1010110000000001101011001000010110101111110010100011011100101010000101011010001110011001011110111"
                binaryString2 = "10100110100001100010010001111100001110100110111001100001011010011000101111001110100011101110000100100010001100110000001010011000100110"
                
                string1.innerText = binaryString1;
                string2.innerText = binaryString2;
                expected.innerText = "10100110100001100010010001111100001111111100111001101110110011011011100101001100111000001001101001110010111000000001111101100100011101"
                result.innerText = addBinaryStrings(binaryString1, binaryString2)
                * {
                  font-family: monospace;
                }
                
                div b {
                  color: red;
                }
                div span {
                  color: dodgerblue;
                }
                <div>BINARY&nbsp;1:&nbsp;<b id="string1"></b></div>
                <div>SUMM&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;<span>+</span></div>
                <div>BINARY&nbsp;2:&nbsp;<b id="string2"></b></div>
                <div>FINALLY&nbsp;:&nbsp;<span>=</span></div>
                <div>EXPECTED:&nbsp;<span id="expected"></span></div>
                <div>RESULT&nbsp;&nbsp;:&nbsp;<span id="result"></span></div>

                【讨论】:

                  【解决方案13】:

                  你可以像下面这样解决它:

                  let addBinary = (a,b)=> parseInt(a,2)*parseInt(b,2).toString(2);
                  

                  就是这样

                  【讨论】:

                    【解决方案14】:

                    parseInt 可能会导致大于最大整数的问题。

                    解决方案是BigInt

                    (BigInt('0b' + a) + BigInt('0b' + b)).toString(2);
                    

                    差不多
                    (parseInt(a, 2) + parseInt(b, 2)).toString(2)
                    

                    但它可以处理更多。

                    【讨论】:

                      【解决方案15】:

                      这是一个简单的解决方案,给定ab 是两个二进制字符串,返回它们的sum 也是二进制字符串。

                      let addBinary = (a, b) => {
                        // handle corner cases when either input is empty
                        if (a === null || a.length === 0) return b;
                        if (b === null || b.length === 0) return a;
                      
                        let i = a.length - 1;
                        let j = b.length - 1;
                        let carry = 0;
                        let sum = '';
                      
                        while (i >=0 || j >= 0) {
                          let x = i < 0 ? 0 : +a[i];
                          let y = j < 0 ? 0 : +b[i];
                          let val = x + y + carry;
                      
                          carry = val > 1 ? 1 : 0;
                          sum = val % 2 + sum;
                      
                          i--;
                          j--;
                        }
                      
                        return carry > 0 ? carry + sum : sum;
                      };
                      

                      关于二进制加法的一些基本规则:

                      • 0 + 0 = 0
                      • 0 + 1 = 1
                      • 1 + 0 = 1
                      • 1 + 1 = 0,进位 = 1

                      希望这会有所帮助!

                      【讨论】:

                      • 如所写,当我们期望"1"时,此函数返回"0"addBinary("0", "1"); // "1"
                      【解决方案16】:

                      添加 2 个 n 长度二进制整数的简单而朴素的解决方案:

                      function binaryAddition(num1, num2) {
                        let result = [];
                        let carry = 0;
                        for(let i = num1.length-1; i >= 0; i--) {
                          if((num1[i] === 1 && num2[i] === 1)) {
                            if(carry) {
                             result.unshift(1) 
                            } else {
                             result.unshift(0);  
                            }
                            carry = 1;
                          } else if((num1[i] === 0 && num2[i] === 0)) {
                            if(carry) {
                             result.unshift(1) 
                            } else {
                             result.unshift(0);  
                            } 
                            carry = 0;
                          } else if((num1[i] === 1 && num2[i] === 0) || (num1[i] === 0 && num2[i] === 1)) {
                            if(carry) {
                             result.unshift(0)
                             carry = 1;
                            } else {
                             result.unshift(1);  
                            }
                          }  
                        }
                        if(carry) result.unshift(1);
                        return result;
                      }
                      

                      【讨论】:

                      • 对于 case addBinary(11, 1); // 100 和 case addBinary(0, 1); // 1 失败
                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2018-09-02
                      • 2012-11-25
                      • 1970-01-01
                      • 2012-01-22
                      • 1970-01-01
                      • 2017-02-13
                      • 1970-01-01
                      相关资源
                      最近更新 更多