如何在JavaScript中将整数转换为二进制?

506

我希望看到以二进制形式表示的整数,可以是正数或负数。

就像这个问题一样,但是针对JavaScript。


5
a.toString(2) 的例子似乎对于 -1 无法正常工作。 - barlop
1
还可以将二进制转换为十进制:https://dev59.com/xGgu5IYBdhLWcg3wv5im - Anderson Green
当我说“用二进制表示”时,这可能有点模糊。我指的是内部位字符串表示,它是2s补码,因此正数将以基数2表示,并附带前导0(负数不会用减号符号或符号大小表示法写出,而是作为其正等价物的函数)。 - barlop
在JavaScript中,二进制中的负零实际上是什么?它只能以1的补码表示吗?通常表示为所有二进制1。-> 111111111.... 不确定有多少个。此参考文献将32位数字的二进制表示形式-0表示为1后跟31个零。- https://en.wikipedia.org/wiki/Signed_zero - JoePythonKing
@JoePythonKing 你说的“它只能是1s complement吗?”是什么意思?你来自英国,也许你可以用更好的英语写。你可以查一下 1s complement 或 2s complement 中的 -1。计算机倾向于使用 2s complement 而不是 1s complement,但从一个转换到另一个并不难。还有一些其他的表示方法。至于负数中有多少个1,在例如 2s complement 中,这并不重要,对于1s complement 也可能不重要。这取决于你有多少位来存储这个数字! - barlop
显示剩余6条评论
17个回答

801

function dec2bin(dec) {
  return (dec >>> 0).toString(2);
}

console.log(dec2bin(1)); // 1
console.log(dec2bin(-1)); // 11111111111111111111111111111111
console.log(dec2bin(256)); // 100000000
console.log(dec2bin(-256)); // 11111111111111111111111100000000

你可以使用Number.toString(2)函数,但在表示负数时会出现一些问题。例如,(-1).toString(2)的输出为"-1"
为了解决这个问题,你可以使用无符号右移位运算符(>>>)将数字强制转换为无符号整数。
如果你运行(-1 >>> 0).toString(2),你将把你的数字向右移动0位,这不会改变数字本身,但它将被表示为无符号整数。上面的代码将正确输出"11111111111111111111111111111111"此问题有进一步的解释。

-3 >>> 0(右移0位)将其参数强制转换为无符号整数,因此您会得到-3的32位二进制补码表示。


11
以下是翻译的内容:这里有一个解释,说明如何将负数转换为二进制字符串:链接 - fernandosavio
2
toString(2) 不起作用,因为您正在从文本中获取输入。请使用以下代码:function decToBase(dec, base){ return parseInt(dec).toString(base); }alert(decToBase(dec, 2)); - Magus
1
你假设输入是文本,但答案中的函数期望一个整数...所以,如果输入是文本,只需将其转换为整数,使用虚拟位移即可完成。 - fernandosavio
@Magus 谁从文本中获取输入?! - barlop
@Magus,这是一个具体的技术问题,而不是关于用户输入的问题。没有人在输入用户,这里也没有文本,如果有的话,那么没有人会挠头想如何将字符串转换为整数,这个问题超出了那个范畴。显然,如果他们确实输入了用户并获得了一个字符串,那么他们可以使用parseInt,但这显然不是问题所在。问题到达了问题的核心,这就是问题应该被提出的方式。问题给出了问题的最纯粹形式,并得到了一个纯粹的答案。因此,我评论说(-3 >>> 0).toString(2)就可以了。每个回答者都理解了这一点,除了你的评论。 - barlop
显示剩余22条评论

307

尝试一下

num.toString(2);

2是基数,可以是2到36之间的任意进制

来源在这里

更新:

这只适用于正数,Javascript用二补码表示负二进制整数。我编写了这个小函数,应该可以解决问题,但我没有正确测试过:

function dec2Bin(dec)
{
    if(dec >= 0) {
        return dec.toString(2);
    }
    else {
        /* Here you could represent the number in 2s compliment but this is not what 
           JS uses as its not sure how many bits are in your number range. There are 
           some suggestions https://dev59.com/DGTWa4cB1Zd3GeqPGLg9 
        */
        return (~dec).toString(2);
    }
}

我从这里得到了一些帮助。


1
不适用于-1。a=-1; document.write(Number(a.toString(2))); 显示为-1。 - barlop
更新仍然似乎无法处理负数(-3 返回 1)。此外,我认为 dec > 0 应该改为 dec >= 0,这至少可以修复 0。因为 dec2Bin(0) 返回 10 - Adam Merrifield
在上述评论中,这两种情况在我的Chrome控制台中都返回了正确的结果 - var a = -1; a.toString(2); "-1" var a = -3; a.toString(2); "-11" - Anmol Saraf
@AnmolSaraf 我明白你的意思,尽管在口语中当人们说十进制下的-5时,答案是-5。但是当涉及到二进制中的负数时,在某种程度上,你可以在那里放一个减号,所以5是101,-5是-101,但由于计算机不存储减号,它们只表示1和0,因此当我们说二进制中的负数时,我们真正意思是将负数(包括减号)放入1和0中。一些方法包括1s补码、2s补码和“符号和大小”。因此,-101010101或-0101010不是人们所说的二进制中的负数的含义。 - barlop
这个链接可能对一些人有兴趣 https://dev59.com/J2ct5IYBdhLWcg3wHZ5q 无论如何,你的回答自相矛盾,你写道“Javascript用二进制补码表示负数整数。”而你的代码却说“在这里你可以用2s补码表示数字,但这不是JS使用的[nonsense reason]”,而且你也没有提供任何参考资料。 - barlop
对我来说,负数应该只有一个负二进制表示。因此,底部应该返回“-”+(-dec).toString(2); - Luc Bloom

89

一个简单的方法就是...

Number(42).toString(2);

// "101010"

33
我会选择(42).toString(2) - Willem D'Haeseleer
47
"42..toString(2)" 的翻译是:把数字 42 转换为二进制并以字符串形式输出。 - kapex
10
人们对此感到困惑。 答案是正确的,因为它将输入(42)转换为整数,并且需要那行代码。如果您从文本输入中获取“数字”,toString(2)将无法使用。 - Magus
5
@Kapep,老兄你太聪明了。你怎么知道那件事的? - Pacerier
8
在数字语法中,你可以省略小数点后面的部分。例如,你可以写1.,它与1.0相同,或者仅写1(同样地,你也可以省略前面部分并写成.5而不是0.5)。所以,在这个例子中,第一个点是小数点,它是数字的一部分,而第二个点是调用该数字方法的点操作符。你必须使用两个点(或将数字括在括号中),不能仅写成42.toString(2),因为解析器会将点视为小数点,并因缺少点操作符而抛出错误。 - kapex
显示剩余5条评论

60

'convert to binary' 中的二进制可以指三个主要方面:位置计数法、内存中的二进制表示或32位比特串。 (对于64位比特串,请参见Patrick Roberts的答案

1. 数字系统

(123456).toString(2)将数字转换为基数为2的位置记数法。在此系统中,负数像十进制数一样以减号写入。

2. 内部表示

数字的内部表示是64位浮点数,有一些限制在这个答案中进行了讨论。在JavaScript中没有简单的方法来创建它的比特串表示,也无法访问特定位。

3. 掩码和按位运算符

MDN提供了良好的概述,介绍了按位运算符的工作原理。重要的是:

按位运算符将它们的操作数视为一系列32位(零和一)

在执行操作之前,64位浮点数会被转换为32位有符号整数。然后它们会被转换回来。

下面是MDN示例代码,用于将数字转换为32位字符串。

function createBinaryString (nMask) {
  // nMask must be between -2147483648 and 2147483647
  for (var nFlag = 0, nShifted = nMask, sMask = ""; nFlag < 32;
       nFlag++, sMask += String(nShifted >>> 31), nShifted <<= 1);
  return sMask;
}

createBinaryString(0) //-> "00000000000000000000000000000000"
createBinaryString(123) //-> "00000000000000000000000001111011"
createBinaryString(-1) //-> "11111111111111111111111111111111"
createBinaryString(-1123456) //-> "11111111111011101101101110000000"
createBinaryString(0x7fffffff) //-> "01111111111111111111111111111111"

1
使用这个函数的优点是什么,而不是使用简单的Number(num).toString(2)? - Magus
6
@Magus 我认为我已经充分解释了数字和二进制字符串之间的区别。一个32位的二进制字符串始终是由"1"和"0"组成,长度为32个字符。toString返回一个使用指定基数的位置数系统表示的实际数字。这取决于你想要字符串的原因,它们具有非常不同的含义。 - AnnanFay
1
使用其他发布的方法时遇到了前导0的问题(特别是在这个数字536870912上,两个前导零被删除了),但是这个解决方案处理得很正确。 - UberMouse
我之所以接受这个答案,是因为它没有缺失的0问题。而且我添加了两个注释。但是当你写到.toString(2)时,“它将数字转换为基于2的位值计数系统。在这个系统中,就像在十进制中一样。” 你确定2的补码不也是基于2的位值计数系统吗?! - barlop
@UberMouse 是的,没错。那么 x=536870912; if(x>0) prepend="0"; else prepend=""; alert(prepend+((x>>>0)).toString(2).toString()); 怎么样? - barlop
显示剩余9条评论

35

这个答案尝试解决绝对值在214748364810 (231) - 900719925474099110 (253-1)范围内的输入。


在JavaScript中,数字以64位浮点表示法存储,但按位操作将它们强制转换为32位整数,使用补码格式,因此使用按位操作的任何方法都会将输出范围限制为-2147483648 10(-2 31)- 214748364710(231 -1)。
然而,如果避免按位操作并仅使用数学运算保留64位浮点表示法,则可以通过符号扩展53位的twosComplement,可靠地将任何安全整数转换为64位二进制补码表示法:

function toBinary (value) {
  if (!Number.isSafeInteger(value)) {
    throw new TypeError('value must be a safe integer');
  }

  const negative = value < 0;
  const twosComplement = negative ? Number.MAX_SAFE_INTEGER + value + 1 : value;
  const signExtend = negative ? '1' : '0';

  return twosComplement.toString(2).padStart(53, '0').padStart(64, signExtend);
}

function format (value) {
  console.log(value.toString().padStart(64));
  console.log(value.toString(2).padStart(64));
  console.log(toBinary(value));
}

format(8);
format(-8);
format(2**33-1);
format(-(2**33-1));
format(2**53-1);
format(-(2**53-1));
format(2**52);
format(-(2**52));
format(2**52+1);
format(-(2**52+1));
.as-console-wrapper{max-height:100%!important}

对于老旧的浏览器,存在以下函数和值的polyfills:

作为额外的奖励,如果您在BigInt中使用两个补码转换来支持任何基数(2-36),并在⌈64 / log2(radix)⌉位数字中执行,则可以实现此功能:

function toRadix (value, radix) {
  if (!Number.isSafeInteger(value)) {
    throw new TypeError('value must be a safe integer');
  }

  const digits = Math.ceil(64 / Math.log2(radix));
  const twosComplement = value < 0
    ? BigInt(radix) ** BigInt(digits) + BigInt(value)
    : value;

  return twosComplement.toString(radix).padStart(digits, '0');
}

console.log(toRadix(0xcba9876543210, 2));
console.log(toRadix(-0xcba9876543210, 2));
console.log(toRadix(0xcba9876543210, 16));
console.log(toRadix(-0xcba9876543210, 16));
console.log(toRadix(0x1032547698bac, 2));
console.log(toRadix(-0x1032547698bac, 2));
console.log(toRadix(0x1032547698bac, 16));
console.log(toRadix(-0x1032547698bac, 16));
.as-console-wrapper{max-height:100%!important}

如果你对我以前的回答感兴趣,该回答使用ArrayBuffer来创建Float64ArrayUint16Array之间的联合,请参考此回答的修订历史


谢谢,很好,这适用于64位。您能告诉我这个答案相比于安南的答案有哪些优势吗? - barlop
3
更大的范围?它适用于“-(231)”到“231-1”之外的数值范围,而不像annan的答案只适用于“-(231)”到“231-1”。 - Patrick Roberts
是的,这是一个很大的优势,我明白,这样做可以实现目标,尽管需要编写更多的代码,但我的意思是,我想知道还有没有其他的优点? - barlop
1
从2 ** 32 + 1开始,当应该设置时,最后(最右边)的位被清除。 - Lovro
1
当该行为:var exponent = ((uint16[3] & 0x7FF0) >> 4) - 1023 + 1;时有效。 - Lovro
显示剩余2条评论

32
一个我会选择的解决方案适用于32位,是这个答案末尾的代码,来自developer.mozilla.org(MDN),但添加了一些行用于A)格式化和B)检查数字是否在范围内。
有人建议使用x.toString(2),但对于负数无效,它只是在其中插入一个减号,这是不好的。
Fernando提到了一个简单的解决方案(x>>>0).toString(2);,对于负数是可以的,但当x为正数时有一个小问题。它的输出以1开头,对于正数来说不是正确的二进制补码。
任何不理解正数以0开头,负数以1开头的二进制补码的人,可以查看这个关于二进制补码的SO问答。什么是“二进制补码”? 一个解决方案可能是在正数前面添加一个0,这是我在之前修订答案时做的。有时候可以接受一个33位的数字,或者确保要转换的数字在范围-(2^31)<=x<2^31-1内。这样数字始终是32位的。但与其这样做,你可以选择使用mozilla.org上的这个解决方案。
Patrick的答案和代码很长,显然适用于64位,但有一个评论者发现了一个bug,并修复了Patrick的bug。但是Patrick的代码中有一个他没有注释的"魔法数字",他已经忘记了并且不再完全理解自己的代码/为什么它能工作。
Annan的术语不正确且不清楚,但提到了developer.mozilla.org上的一个解决方案。
注意-旧链接https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators现在重定向到其他地方,没有那个内容,但是适当的旧链接,当archive.org检索页面时会出现!,在这里可用https://web.archive.org/web/20150315015832/https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators
那里的解决方案适用于32位数字。
代码非常紧凑,只有三行的函数。
但是我已经添加了一个正则表达式来将输出格式化为8位一组。基于如何使用逗号作为千位分隔符格式化数字?(我只是将其从从右到左以3位为一组并添加逗号的方式修改为从右到左以8位为一组并添加空格的方式)
而且,尽管Mozilla对nMask(输入的数字)的大小发表了评论...即它必须在范围内,但他们没有测试或在数字超出范围时抛出错误,所以我添加了这个。
关于为什么他们将参数命名为'nMask',我不确定,但评论者Magne提到它“可能是因为整数被视为位掩码,而nMask则指的是多个掩码(一个或多个掩码组合成一个)。请参阅Mozille位运算符链接中的“自动创建掩码”部分。”

https://web.archive.org/web/20150315015832/https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators

function createBinaryString(nMask) {
  // nMask must be between -2147483648 and 2147483647
  if (nMask > 2**31-1) 
     throw "number too large. number shouldn't be > 2**31-1"; //added
  if (nMask < -1*(2**31))
     throw "number too far negative, number shouldn't be < -(2**31)" //added
  for (var nFlag = 0, nShifted = nMask, sMask = ''; nFlag < 32;
       nFlag++, sMask += String(nShifted >>> 31), nShifted <<= 1);
  sMask=sMask.replace(/\B(?=(.{8})+(?!.))/g, " ") // added
  return sMask;
}


console.log(createBinaryString(-1))    // "11111111 11111111 11111111 11111111"
console.log(createBinaryString(1024))  // "00000000 00000000 00000100 00000000"
console.log(createBinaryString(-2))    // "11111111 11111111 11111111 11111110"
console.log(createBinaryString(-1024)) // "11111111 11111111 11111100 00000000"

//added further console.log example
console.log(createBinaryString(2**31 -1)) //"01111111 11111111 11111111 11111111"  


nMask 的名称可能是因为整数被视为 _位掩码_,然后 nMask 指的是 _多个掩码_(一个或多个掩码集合成一个)。请参见“自动创建掩码”一节:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators - Magne
1
@Magne 谢谢,我现在已经将那部分内容融入到我的回答中了。 - undefined

10

您可以编写自己的函数,返回一个位数组。

将数字转换为位的示例:

除数| 被除数| 位/余数

2 | 9 | 1

2 | 4 | 0

2 | 2 | 0

~ | 1 |~

上述行的示例: 2 * 4 = 8,余数为 1,因此 9 = 1 0 0 1。

function numToBit(num){
    var number = num
    var result = []
    while(number >= 1 ){
        result.unshift(Math.floor(number%2))
        number = number/2
    }
    return result
}

从底部到顶部读取剩余部分。数字1位于中间到顶部。


1
顺便问一下,为什么你要用 Math.floor(number%2) 而不是 number = Math.floor(number/2) - Pacerier
2
原因是number%2不等于number/2。我们感兴趣的是余数而不是商。 - supritshah1289

5
这是我处理它的方法:
const decbin = nbr => {
  if(nbr < 0){
     nbr = 0xFFFFFFFF + nbr + 1
  }
  return parseInt(nbr, 10).toString(2)
};

从此链接获取:https://locutus.io/php/math/decbin/


@barlop 感谢您提出这些问题,现在已经进行了编辑。 - gildniy
1
你能解释一下那段代码的逻辑,它是如何工作的吗?通过将0xFFFFFFFF+1添加到负数中,它是如何运作的...如果你从某个地方得到了这段代码,能否提供一个链接?谢谢。 - barlop
@barlop,从这个链接 https://locutus.io/php/math/decbin/ 得到了它。 - gildniy

2
我们还可以如下计算正数或负数的二进制:

function toBinary(n){
    let binary = "";
    if (n < 0) {
      n = n >>> 0;
    }
    while(Math.ceil(n/2) > 0){
        binary = n%2 + binary;
        n = Math.floor(n/2);
    }
    return binary;
}

console.log(toBinary(7));
console.log(toBinary(-7));


质疑想要否定太多 - barlop
将负数转换为无符号表示。现在逻辑将适用于正数或负数。感谢@barlop。 - ganesh phirke
将从1开始的正数(例如7)表示为111是一个问题。因为如果你想要正数从1开始,那么你如何知道111是什么,它是7还是-1。你的程序将-1表示为11111111111111111111111111111111,将7表示为111。在二进制补码中,1111111和111表示同一个数字,即-1。 - barlop

2
你可以使用递归解决方案:

function intToBinary(number, res = "") {
  if (number < 1)
    if (res === "") return "0"
      else 
     return res
  else return intToBinary(Math.floor(number / 2), number % 2 + res)
}
console.log(intToBinary(12))
console.log(intToBinary(546))
console.log(intToBinary(0))
console.log(intToBinary(125))

Works only with positive numbers.


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接