Why does bitwise "not 1" equal -2? - javascript

Suppose we have 1 and this number in base 2 is:
00000000000000000000000000000001
Now I want to flip all bits to get following result:
11111111111111111111111111111110
As far as I know, the solution is to use the ~ (bitwise NOT operator) to flip all bits, but the result of ~1 is -2:
console.log(~1); //-2
console.log((~1).toString(2)); //-10 (binary representation)
Why do I get this strange result?

There are 2 integers between 1 and -2: 0 and -1
1 in binary is 00000000000000000000000000000001
0 in binary is 00000000000000000000000000000000
-1 in binary is 11111111111111111111111111111111
-2 in binary is 11111111111111111111111111111110
("binary" being 2's complement, in the case of a bitwise not ~ )
As you can see, it's not very surprising ~1 equals -2, since ~0 equals -1.
As #Derek explained, These bitwise operators treat their operands as a sequence of 32 bits. parseInt, on the other hand, does not. That is why you get some different results.
Here's a more complete demo:
for (var i = 5; i >= -5; i--) {
console.log('Decimal: ' + pad(i, 3, ' ') + ' | Binary: ' + bin(i));
if (i === 0)
console.log('Decimal: -0 | Binary: ' + bin(-0)); // There is no `-0`
}
function pad(num, length, char) {
var out = num.toString();
while (out.length < length)
out = char + out;
return out
}
function bin(bin) {
return pad((bin >>> 0).toString(2), 32, '0');
}
.as-console-wrapper { max-height: 100% !important; top: 0; }

100 -4
101 -3
110 -2
111 -1
000 0
001 1
010 2
011 3
A simple way to remeber how two's complement notation works is imagine it's just a normal binary, except its last bit corresponds to the same value negated. In my contrived three-bit two's complement first bit is 1, second is 2, third is -4 (note the minus).
So as you can see, a bitwise not in two's complement is -(n + 1). Surprisingly enough, applying it to a number twice gives the same number:
-(-(n + 1) + 1) = (n + 1) - 1 = n
It is obvious when talking bitwise, but not so much in its arithmetical effect.
Several more observations that make remebering how it works a bit easier:
Notice how negative values ascend. Quite the same rules, with just 0 and 1 swapped. Bitwise NOTted, if you will.
100 -4 011 - I bitwise NOTted this half
101 -3 010
110 -2 001
111 -1 000
----------- - Note the symmetry of the last column
000 0 000
001 1 001
010 2 010
011 3 011 - This one's left as-is
By cycling that list of binaries by half of the total amount of numbers in there, you get a typical sequence of ascending binary numbers starting at zero.
- 100 -4 \
- 101 -3 |
- 110 -2 |-\ - these are in effect in signed types
- 111 -1 / |
*************|
000 0 |
001 1 |
010 2 |
011 3 |
*************|
+ 100 4 \ |
+ 101 5 |-/ - these are in effect in unsigned types
+ 110 6 |
+ 111 7 /

In computer science it's all about interpretation. For a computer everything is a sequence of bits that can be interpreted in many ways. For example 0100001 can be either the number 33 or ! (that's how ASCII maps this bit sequence).
Everything is a bit sequence for a computer, no matter if you see it as a digit, number, letter, text, Word document, pixel on your screen, displayed image or a JPG file on your hard drive. If you know how to interpret that bit sequence, it may be turned into something meaningful for a human, but in the RAM and CPU there are only bits.
So when you want to store a number in a computer, you have to encode it. For non-negative numbers it's pretty simple, you just have to use binary representation. But how about negative numbers?
You can use an encoding called two's complement. In this encoding you have to decide how many bits each number will have (for example 8 bits). The most significant bit is reserved as a sign bit. If it's 0, then the number should be interpreted as non-negative, otherwise it's negative. Other 7 bits contain actual number.
00000000 means zero, just like for unsigned numbers. 00000001 is one, 00000010 is two and so on. The largest positive number that you can store on 8 bits in two's complement is 127 (01111111).
The next binary number (10000000) is -128. It may seem strange, but in a second I'll explain why it makes sense. 10000001 is -127, 10000010 is -126 and so on. 11111111 is -1.
Why do we use such strange encoding? Because of its interesting properties. Specifically, while performing addition and subtraction the CPU doesn't have to know that it's a signed number stored as two's complement. It can interpret both numbers as unsigned, add them together and the result will be correct.
Let's try this: -5 + 5. -5 is 11111011, 5 is 00000101.
11111011
+ 00000101
----------
000000000
The result is 9 bits long. Most significant bit overflows and we're left with 00000000 which is 0. It seems to work.
Another example: 23 + -7. 23 is 00010111, -7 is 11111001.
00010111
+ 11111001
----------
100010000
Again, the MSB is lost and we get 00010000 == 16. It works!
That's how two's complement works. Computers use it internally to store signed integers.
You may have noticed that in two's complements when you negate bits of a number N, it turns into -N-1. Examples:
0 negated == ~00000000 == 11111111 == -1
1 negated == ~00000001 == 11111110 == -2
127 negated == ~01111111 == 10000000 == -128
128 negated == ~10000000 == 01111111 == 127
This is exactly what you have observed: JS is pretending it's using two's complement. So why parseInt('11111111111111111111111111111110', 2) is 4294967294? Well, because it's only pretending.
Internally JS always uses floating point number representation. It works in a completely different way than two's complement and its bitwise negation is mostly useless, so JS pretends a number is two's complement, then negates its bits and converts it back to floating point representation. This does not happen with parseInt, so you get 4294967294, even though binary value is seemingly the same.

A 2's complement 32 bit signed integer (Javascript insists that is the format used for a 32 bit signed integer) will store -2 as 11111111111111111111111111111110
So all as expected.

It's two's complement arithmetic. Which is the equivalent of "tape counter" arithmetic. Tape recorders tended to have counters attached (adding machines would likely be an even better analogy but they were obsolete already when 2s complement became hip).
When you wind backwards 2 steps from 000, you arrive at 998. So 998 is the tape counter's 10s complement arithmetic representation for -2: wind forward 2 steps, arrive at 000 again.
2s complement is just like that. Wind forward 1 from 1111111111111111 and you arrive at 0000000000000000, so 1111111111111111 is the representation of -1. Wind instead back another 1 from there, and you get 1111111111111110 which then is the representation of -2.

Numbers in JavaScript are floating point numbers, stored and represented by IEEE 754 standard.
However, for bitwise operations, the operands are internally treated as signed 32-bit integers represented by two's complement format:
The operands of all bitwise operators are converted to signed 32-bit
integers in two's complement format. Two's complement format means
that a number's negative counterpart (e.g. 5 vs. -5) is all the
number's bits inverted (bitwise NOT of the number, a.k.a. ones'
complement of the number) plus one.
A negative number's positive counterpart is calculated the same way. Thus we have:
1 = 00000000000000000000000000000001b
~1 = 11111111111111111111111111111110b
11111111111111111111111111111110b = -2
Note that Number.toString() is not supposed to return the two's complement representation for base-2.
The expression (-2).toString(2) yields -10 which is the minus sign (-) followed by base-2 representation of 2 (10).

This is the expected behavior. According to mdn:bitwise-not.
The part you probably don't understand is that
[11111111111111111111111111111110]₂ = [10]₂¹, if expressed as a signed integer. The leading 1s can be as many as you want and it's still the same number, similar to leading 0s in unsigned integers/decimal.
¹ [10]₂ specifies that 10 should be interpreted as base 2 (binary)

Related

.split() replacing last few elements of array [duplicate]

See this code:
var jsonString = '{"id":714341252076979033,"type":"FUZZY"}';
var jsonParsed = JSON.parse(jsonString);
console.log(jsonString, jsonParsed);
When I see my console in Firefox 3.5, the value of jsonParsed is the number rounded:
Object id=714341252076979100 type=FUZZY
Tried different values, the same outcome (number rounded).
I also don't get its rounding rules. 714341252076979136 is rounded to 714341252076979200, whereas 714341252076979135 is rounded to 714341252076979100.
Why is this happening?
You're overflowing the capacity of JavaScript's number type, see §8.5 of the spec for details. Those IDs will need to be strings.
IEEE-754 double-precision floating point (the kind of number JavaScript uses) can't precisely represent all numbers (of course). Famously, 0.1 + 0.2 == 0.3 is false. That can affect whole numbers just like it affects fractional numbers; it starts once you get above 9,007,199,254,740,991 (Number.MAX_SAFE_INTEGER).
Beyond Number.MAX_SAFE_INTEGER + 1 (9007199254740992), the IEEE-754 floating-point format can no longer represent every consecutive integer. 9007199254740991 + 1 is 9007199254740992, but 9007199254740992 + 1 is also 9007199254740992 because 9007199254740993 cannot be represented in the format. The next that can be is 9007199254740994. Then 9007199254740995 can't be, but 9007199254740996 can.
The reason is we've run out of bits, so we no longer have a 1s bit; the lowest-order bit now represents multiples of 2. Eventually, if we keep going, we lose that bit and only work in multiples of 4. And so on.
Your values are well above that threshold, and so they get rounded to the nearest representable value.
As of ES2020, you can use BigInt for integers that are arbitrarily large, but there is no JSON representation for them. You could use strings and a reviver function:
const jsonString = '{"id":"714341252076979033","type":"FUZZY"}';
// Note it's a string −−−−^−−−−−−−−−−−−−−−−−−^
const obj = JSON.parse(jsonString, (key, value) => {
if (key === "id" && typeof value === "string" && value.match(/^\d+$/)) {
return BigInt(value);
}
return value;
});
console.log(obj);
(Look in the real console, the snippets console doesn't understand BigInt.)
If you're curious about the bits, here's what happens: An IEEE-754 binary double-precision floating-point number has a sign bit, 11 bits of exponent (which defines the overall scale of the number, as a power of 2 [because this is a binary format]), and 52 bits of significand (but the format is so clever it gets 53 bits of precision out of those 52 bits). How the exponent is used is complicated (described here), but in very vague terms, if we add one to the exponent, the value of the significand is doubled, since the exponent is used for powers of 2 (again, caveat there, it's not direct, there's cleverness in there).
So let's look at the value 9007199254740991 (aka, Number.MAX_SAFE_INTEGER):
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit
/ +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent
/ / | +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand
/ / | / |
0 10000110011 1111111111111111111111111111111111111111111111111111
= 9007199254740991 (Number.MAX_SAFE_INTEGER)
That exponent value, 10000110011, means that every time we add one to the significand, the number represented goes up by 1 (the whole number 1, we lost the ability to represent fractional numbers much earlier).
But now that significand is full. To go past that number, we have to increase the exponent, which means that if we add one to the significand, the value of the number represented goes up by 2, not 1 (because the exponent is applied to 2, the base of this binary floating point number):
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit
/ +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent
/ / | +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand
/ / | / |
0 10000110100 0000000000000000000000000000000000000000000000000000
= 9007199254740992 (Number.MAX_SAFE_INTEGER + 1)
Well, that's okay, because 9007199254740991 + 1 is 9007199254740992 anyway. But! We can't represent 9007199254740993. We've run out of bits. If we add just 1 to the significand, it adds 2 to the value:
+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit
/ +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent
/ / | +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand
/ / | / |
0 10000110100 0000000000000000000000000000000000000000000000000001
= 9007199254740994 (Number.MAX_SAFE_INTEGER + 3)
The format just cannot represent odd numbers anymore as we increase the value, the exponent is too big.
Eventually, we run out of significand bits again and have to increase the exponent, so we end up only being able to represent multiples of 4. Then multiples of 8. Then multiples of 16. And so on.
What you're seeing here is actually the effect of two roundings. Numbers in ECMAScript are internally represented double-precision floating-point. When id is set to 714341252076979033 (0x9e9d9958274c359 in hex), it actually is assigned the nearest representable double-precision value, which is 714341252076979072 (0x9e9d9958274c380). When you print out the value, it is being rounded to 15 significant decimal digits, which gives 14341252076979100.
It is not caused by this json parser. Just try to enter 714341252076979033 to fbug's console. You'll see the same 714341252076979100.
See this blog post for details:
http://www.exploringbinary.com/print-precision-of-floating-point-integers-varies-too
JavaScript uses double precision floating point values, ie a total precision of 53 bits, but you need
ceil(lb 714341252076979033) = 60
bits to exactly represent the value.
The nearest exactly representable number is 714341252076979072 (write the original number in binary, replace the last 7 digits with 0 and round up because the highest replaced digit was 1).
You'll get 714341252076979100 instead of this number because ToString() as described by ECMA-262, §9.8.1 works with powers of ten and in 53 bit precision all these numbers are equal.
The problem is that your number requires a greater precision than JavaScript has.
Can you send the number as a string? Separated in two parts?
JavaScript can only handle exact whole numbers up to about 9000 million million (that's 9 with 15 zeros). Higher than that and you get garbage. Work around this by using strings to hold the numbers. If you need to do math with these numbers, write your own functions or see if you can find a library for them: I suggest the former as I don't like the libraries I've seen. To get you started, see two of my functions at another answer.

Confusion with left-shift operator

I am trying to create a 32-bit bitmask in JS. However, I don't understand what's happening here:
$ node
> const num = Math.pow(2, 31) - 1
undefined
> num
2147483647
# So far, so good
> num.toString(2)
'1111111111111111111111111111111'
> num.toString(2).length
31
# According to MDN, the left-shift operator does:
# "Excess bits shifted off to the left are discarded.
# Zero bits are shifted in from the right."
# But that's not what we see here. Instead, it seems to be wrapping.
> num << 1
-2
> (num << 1).toString(2)
'-10'
According to my understanding of the MDN docs, I'd expect to have a bitmask of 31 1s followed by 1 0. Instead, I get -10. What's going on here?
Javascript doesn't have Integer, but bitwise operators only make sense on Integer.
So before bitwise operators, javascript will apply ToInt32(val) to your num.
For "Signed 32 bits Integer", the top bit represents 'signed'.
Finally your num overflow the 'signed bit'.
My English is poor, you can check ECMAScript's Language Specification.
It is not wrapping. It is exactly working as the documentation you've linked. In your linked documentation, it says:
The operands of all bitwise operators are converted to signed 32-bit integers in two's complement format.
Your num is 2147483647, and this number in two's complement format is:
01111111 11111111 11111111 11111111
After left shifting num by 1, it becomes:
11111111 11111111 11111111 11111110
The above 32-bit two's complement value is -2 in decimal number. Hence, you get:
> num
2147483647
> num << 1
-2
If you call toString(2) with a negative decimal number -2, it follows some special rule of toString(). What it does is:
Take the magitude of the decimal. (-2 => 2)
Convert the decimal to base-2 string representation. (2 => '10')
Put the minus sign in front. ('10' => '-10')
Therefore, you get:
> (num << 1).toString(2)
> -10
You also get:
> (-2).toString(2)
> -10
Today I check your question again then know I misunderstand your main point yesterday.
This is your main point:
" I'd expect to have a bitmask of 31 1s followed by 1 0. Instead, I get -10."
Because Two's complement.
A negative number doesn't express value directly.
let num=Math.pow(2,31)-1
//num in memory=> 0111111111111111111111111111111
// ^
// |
// signed bit
//This number is positive, so
//nothing to do=> 0111111111111111111111111111111
num = num<<1
//num in memory=> 1111111111111111111111111111110
// ^
// |
// signed bit
//This number is negative, so flip then add 1
//flip => 1000000000000000000000000000001
//add 1 => 1000000000000000000000000000010 => '-10'

Why doesn't a >>> 100 produce 0 in JavaScript?

So I was playing around with shifts in the console, and the results have me stumped.
Input:
a = -1
a >>> 100
Output:
268435455
I looked on the Mozilla reference page about it, but it mentions nothing about the behavior of >>> when you shift by large amounts. I assumed that shifting all the bits to the right with zero-fill would result in a zero.
Is this a bug in Firefox or something?
It seems you can only shift by a maximum of 31.
From the site you linked in your post (MDN):
Shifts a in binary representation b (< 32) bits to the right,
discarding bits shifted off, and shifting in zeros from the left.
From the actual spec (Page 77)
Let shiftCount be the result of masking out all but the least
significant 5 bits of rnum, that is, compute rnum & 0x1F.
What's actually happening is when you shift by 100 it shifts by (100 & 0x1F) or 4.
-1 >>> 100 === -1 >>> 4
If you were to split it up into multiple shifts then it will work:
-1 >>> 25 >>> 25 >>> 25 >>> 25 === 0
Any bitwise operator on a Number in JavaScript will convert its operand to a 32 bit big-endian signed number.
This means that if the number is larger than what can be stored by 32 bits, it will be truncated. Big-endian means that number are stored in natural order when reading it from left to right, i.e. more significant numbers are stored first, so if the number is stored over one byte, its first byte is the more significant.
This means that -1's binary representation will be...
11111111 11111111 11111111 11111111
(This is -1 in two's complement. This is performed by calculating the number's value in binary, and then flipping each bit and adding one.)
When you shift by 100, you will find it only shifts by 4, leaving you with...
00001111 11111111 11111111 11111111
As you can see, the high bit is no longer set, so it's not negative, and it is in fact 268435455 (the number from your question).

javascript bitwise operator question

In Javascript when I do this
var num = 1;
~ num == -2
why does ~num not equal 0
in binary 1 is stored as 1 ... thus not 1 should be 0
or it is stored like 0001 thus not 0001 would be 1110
I think I am missing something... can someone clear this up
Look up Two's complement for signed binary numbers
Lets assume that a javascript Number is 8 bits wide (which its not):
then
1 = 0000 0001b
and
~1 = 1111 1110b
Which is the binary representation of -2
0000 0010b = 2
0000 0001b = 1
0000 0000b = 0
1111 1111b = -1
1111 1110b = -2
~ toggles the bits of the operand so
00000001
becomes
11111110
which is -2
Note: In javascript, the numbers are 32-bit, but I shortened it to illustrate the point.
From the documentation:
Bitwise NOTing any number x yields -(x + 1). For example, ~5 yields -6.
The reason for this is that using a bitwise NOT reverses all the bits of a value. If you are storing the value of 1 in a signed 8-bit integer, you're storing the binary value 00000001. If you apply a bitwise NOT, you get 11111110, which for a signed 8-bit integer is the binary value for -2.

Bitwise operator x >> 1 and x >> 0 [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicates:
What do these operators do?
>> in javascript
Can somebody please explain the bitwise operator >> 1?
example:
65 >> 1 = 32
and also when >> 0
what does it achieve in this example:
var size = (Math.random() * 100 >> 0) + 20;
var size = (Math.random() * 100 >> 0) + 20;
>> 0 in the above example is used to eliminate the fractional portion, as follows:
Math.random() returns a number between 0 and 0.99999999...
This number multiplied by 100 gives you another number between 0 and 99.999999...
This number is right shifted 0 times. The number is implicitly cast to an integer for the shift operation; right shifting 0 times does not have any effect on the value of the resulting integer. You thus end up with an integer between 0 and 99. Note that you could have used the Math.floor() function instead of >> 0.
Add 20 to the integer, the result is an integer between 20 and 119.
Bitwise operator >> means shift right.
It moves the binary value to the right (and removes the right-most bit).
65 >> 1 in binary is:
1000001 >> 1 = 100000 = 32
It effectively divides the number into 2 and drops the remainder.
The operator '>>' shifts the contents of a variable right by 1 bit. This results, effectively, in integer division of that value by 2 as you show in your example:
65 >> 1 = 32
Let's say that a variable is always 32 bits long. The example then says:
65 decimal >> 1 = 32 or, in hex, 0x000041 >> 1 = 0x00000020
More generally: the operator '>>' divides its operand, as a 32-bit integer, by the power of 2 whose value is the shift length. Thus:
129 decimal >> 1 = 64 or 0x000081 >> 1 = 0x000040
129 decimal >> 2 = 32 or 0x000081 >> 2 = 0x000020
129 decimal >> 5 = 2 or 0x000081 >> 5 = 0x000002
and
129 decimal >> 8 = 0 or: 0x000081 >> 8 = 0x000000
The operator '<<' multiplies its operand, as you'd expect.
I don't know how Math.random( ) operates, but I'm willing to bet that the shift of its floating-point returned value right by 0 turns that number into an integer, because shifting left and right has arithmetic meaning only when the operand is an integer.
The bitwise shift operator shifts each bit of the input x bits to the right (>>) or to the left (<<).
65 is 1000001, thus 65 >> 1 = 0100000, which is 32.
EDIT
Here are some useful links:
http://en.wikipedia.org/wiki/Bitwise_operation
http://javascript.about.com/library/blbitop.htm
http://www.java2s.com/Tutorial/JavaScript/0040__Operators/ShiftLeft.htm
>> X takes the binary number and moves all the digits right by X places.
In your example, you use 65, which is 01000001 in binary. If you shift that right by one, the first space (on the left) gets filled in with a 0, and the last digit 'falls off the end'. Giving 00100000, which is the binary representation for 32.
>> 0, therefore shifts the number 0 spaces to the right, and does nothing.
'<< X', does the same, but shifts the number to the left.
These can be compared to multiplying by 2^X (Left-shift) or divinding by 2^X (right-shift), but it should be noted that a binary shift is much faster than a division operation.
You can understand why the output is 32 from rsplak's post. >> is the Right Bit Shift operator and using it as >> 1 will cause every bit to be shifted one place to the right. This means, if the rightmost bit was 1, it would get expelled and the left most bit will contain 0.
The bitwise operator shifts an expression by a number of digits. So in your example you have
65 which ist binary 0100 0001 shiftet 1 position to the right so you got 0010 0000 which is 32 decimal.
Another example:
48 >> 3 = 6
48 decimal is 0011 0000 binary shifted 3 to the right is 0000 0110 which is 6 decimal.
For your second example I can not help you - I can not image why I would shift an expression by 0 positions but maybe you can find out debugging it?

Categories

Resources