Negative numbers to binary string in JavaScript
-3 >>> 0 (right logical shift) coerces its arguments to unsigned integers, which is why you get the 32-bit two's complement representation of -3.
http://en.wikipedia.org/wiki/Two%27s_complement
http://en.wikipedia.org/wiki/Logical_shift
.toString()
is designed to return the sign of the number in the string representation. See EcmaScript 2015, section 7.1.12.1:
- If m is less than zero, return the String concatenation of the String "-" and ToString(−m).
This rule is no different for when a radix is passed as argument, as can be concluded from section 20.1.3.6:
- Return the String representation of this Number value using the radix specified by radixNumber. [...] the algorithm should be a generalization of that specified in 7.1.12.1.
Once that is understood, the surprising thing is more as to why it does not do the same with -3 >>> 0
.
But that behaviour has actually nothing to do with .toString(2)
, as the value is already different before calling it:
console.log (-3 >>> 0); // 4294967293
It is the consequence of how the >>>
operator behaves.
It does not help either that (at the time of writing) the information on mdn is not entirely correct. It says:
The operands of all bitwise operators are converted to signed 32-bit integers in two's complement format.
But this is not true for all bitwise operators. The >>>
operator is an exception to the rule. This is clear from the evaluation process specified in EcmaScript 2015, section 12.5.8.1:
- Let lnum be ToUint32(lval).
The ToUint32 operation has a step where the operand is mapped into the unsigned 32 bit range:
- Let int32bit be int modulo 232.
When you apply the above mentioned modulo operation (not to be confused with JavaScript's %
operator) to the example value of -3, you get indeed 4294967293.
As -3 and 4294967293 are evidently not the same number, it is no surprise that (-3).toString(2)
is not the same as (4294967293).toString(2)
.
Short answer:
The
toString()
function takes the decimal, converts it to binary and adds a "-" sign.A zero fill right shift converts it's operands to signed 32-bit integers in two complements format.
A more detailed answer:
Question 1:
//If you try
(-3).toString(2); //show "-11"
It's in the function .toString()
. When you output a number via .toString()
:
Syntax
numObj.toString([radix])
If the numObj is negative, the sign is preserved. This is the case even if the radix is 2; the string returned is the positive binary representation of the numObj preceded by a - sign, not the two's complement of the numObj.
It takes the decimal, converts it to binary and adds a "-" sign.
- Base 10 "3" converted to base 2 is "11"
- Add a sign gives us "-11"
Question 2:
// but if you fake a bit shift operation it works as expected
(-3 >>> 0).toString(2); // print "11111111111111111111111111111101"
A zero fill right shift converts it's operands to signed 32-bit integers. The result of that operation is always an unsigned 32-bit integer.
The operands of all bitwise operators are converted to signed 32-bit integers in two's complement format.
var binary = (-3 >>> 0).toString(2); // coerced to uint32
console.log(binary);
console.log(parseInt(binary, 2) >> 0); // to int32
on jsfiddle
output is
11111111111111111111111111111101
-3