任何人都知道为什么javascript Number.toString
函数不能正确表示负数?
//If you try
(-3).toString(2); //shows "-11"
// but if you fake a bit shift operation it works as expected
(-3 >>> 0).toString(2); // print "11111111111111111111111111111101"
我真的好奇为什么它不能正常工作或者它以这种方式工作的原因是什么? 我搜索了它,但没有找到任何有用的东西。
答案 0 :(得分:26)
-3>>> 0(右逻辑移位)将其参数强制转换为无符号整数,这就是为什么得到-3的32位二进制补码表示。
答案 1 :(得分:17)
var binary = (-3 >>> 0).toString(2); // coerced to uint32
console.log(binary);
console.log(parseInt(binary, 2) >> 0); // to int32
on jsfiddle
输出
11111111111111111111111111111101
-3
答案 2 :(得分:14)
toString()
函数基本上取小数,转换它
二进制并添加" - "登录。
零填充右移将其操作数转换为32个有符号位 两种补充格式的整数。
问题1:
//If you try
(-3).toString(2); //show "-11"
它位于.toString()
函数中。当您通过.toString()
:
语法
numObj.toString([基数])
如果numObj为负数,则会保留该符号。就是这种情况 即使基数为2;返回的字符串是正二进制文件 numObj的表示前面带有 - 符号,而不是两个符号 numObj的补充。
所以基本上它需要十进制,将其转换为二进制并添加一个" - "标志。
问题2:
// but if you fake a bit shift operation it works as expected
(-3 >>> 0).toString(2); // print "11111111111111111111111111111101"
零填充右移将其操作数转换为32 signed bit integer。
所有位运算符的操作数都转换为带符号的32位 两个补码格式的整数。
答案 3 :(得分:1)
如果其他答案有点令人困惑,请在这里总结一下几点:
(-3 >>> 0).toString(2)
,我们称之为A,可以完成工作;但是我们想知道它为什么以及如何工作var num = -3; num.toString(-3)
,我们将得到-11
,它只是数字3的无符号二进制表示形式,前面带有负号,这不是我们想要的 1)(-3 >>> 0)
>>>
操作采用左操作数(-3),它是一个有符号整数,并简单地将位0位置向左移动(因此这些位保持不变),以及与之对应的无符号数不变的位。
有符号数-3的位序列与无符号数4294967293的位序列相同,如果我们在REPL中简单地键入-3 >>> 0
,则节点将为我们提供该位序列。
2)(-3 >>> 0).toString
现在,如果我们对这个无符号数字调用toString
,我们将只获得该数字的位的字符串表示形式,这与-3的位序列相同。
我们实际上所做的是说“嘿toString,当我告诉您打印出无符号整数的位时,您的行为正常,因此,由于我要打印出有符号整数,因此我将其转换为无符号整数,然后为我打印出这些位。”
答案 4 :(得分:0)
.toString()
用于返回字符串表示形式中数字的符号。参见EcmaScript 2015, section 7.1.12.1:
- 如果 m 小于零,则返回字符串“-”和ToString(- m )的字符串串联。
对于将基数作为参数传递时,此规则没有什么不同,可以从section 20.1.3.6得出结论:
- 使用 radixNumber 指定的基数返回此Number值的字符串表示形式。 [...]该算法应该是7.1.12.1中指定的算法的概括。
一旦被理解,令人惊讶的事情更多是关于为什么它与-3 >>> 0
不一样。
但是 行为实际上与.toString(2)
无关,因为在调用它之前,该值已经不同:
console.log (-3 >>> 0); // 4294967293
这是>>>
运算符行为的结果。
(在撰写本文时)mdn上的信息也不完全正确也无济于事。它说:
所有按位运算符的操作数均以二进制补码格式转换为带符号的32位整数。
但是对于 all 所有按位运算符而言,情况并非如此。 >>>
运算符是该规则的例外。根据{{3}}中指定的评估过程,这很明显:
- 让 lnum 为ToUint32( lval )。
EcmaScript 2015, section 12.5.8.1操作具有将操作数映射到32位无符号范围的步骤:
- 让 int32bit 为 int 模2 32 。
将上述模运算(不是JavaScript的%
运算符用于ToUint32)应用于示例值-3时,的确是4294967293。
由于-3和4294967293显然不是相同的数字,因此(-3).toString(2)
与(4294967293).toString(2)
不相同也就不足为奇了。
答案 5 :(得分:0)
Daan's answer 解释得很好。
toString(2)
并没有真正将数字转换为二进制补码,而只是将数字简单地转换为其正二进制形式,同时保留它的符号。
示例
Assume the given input is -15,
1. negative sign will be preserved
2. `15` in binary is 1111, therefore (-15).toString(2) gives output
-1111 (this is not in 2's complement!)
我们知道在 32 位中 -15 的 2 的补码是
11111111 11111111 11111111 11110001
因此为了得到(-15)的二进制形式,我们实际上可以使用无符号右移>>>
将其转换为无符号的32位整数,然后将其传递给toString(2)
打印出来二进制形式。这就是我们执行 (-15 >>> 0).toString(2)
的原因,它将为我们提供 11111111111111111111111111110001
,即 2 的补码中 -15 的正确二进制表示。