Why bitwise shift acts differently when sequenced? - javascript

Why there are different results of bitwise left shift?
1 << 32; # 1
1 << 31 << 1; # 0

That's because of
Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.
of how the << operation is defined. See http://www.ecma-international.org/ecma-262/6.0/#sec-left-shift-operator-runtime-semantics-evaluation
So according to it - 32 & 0x1F equals 0
So 1 << 32 equals to 1 << 0 so is basically no op.
Whereas 2 consecutive shifts by 31 and 1 literally perform calculations

JavaScript defines a left-shift by 32 to do nothing, presumably because it smacks up against the 32-bit boundary. You cannot actually shift anything more than 31 bits across.
Your approach of first shifting 31 bits, then a final bit, works around JavaScript thinking that shifting so much doesn't make sense. Indeed, it's pointless to execute those calculations when you could just write = 0 in the first place.

The reason is that the shift count is considered modulo 32.
This itself happens because (my guess) this is how most common hardware for desktops/laptops works today (x86).
This itself happens because.... well, just because.
These shift limitations are indeed in some cases annoying... for example it would have been better in my opionion to have just one shift operator, working in both directions depending on the sign of the count (like ASH works for Common Lisp).

Related

Big numbers and floating point numbers in JavaScript

I have this Java code:
nextDouble = 0.2515933907977884;
long numerator = (long) (nextDouble * (1L << 53));
I would like to be able to produce the same output this line produces in Java but within JavaScript.
nextDouble = 0.2515933907977884;
const numerator = nextDouble * (1 << 53);
Has anybody got an idea for how to replicate a long within JavaScript ? I know there is BigInt in JavaScript but thing is, it doesn't support floating point numbers, so I am a bit stuck on what to do. Does anybody know any interesting libraries, that could solve this issue ?
Thank you in advance !
The problem is what 1<<53 does in javascript. It doesn't do what you think it does.
Here, try this in the console:
1<<30
1073741824
// okay.. looks good
1<<31
> -2147483648
// a negative num.. wha??
1<<32
> 1
// WHAA????????
1<<53
> 2097152
// That seems VERY low to me
1<<21
> 2097152
// How is that the same? as 1<<53??
numbers in javascript are forced into being doubles, and doing bit shifts on a double is utterly ridiculous. Javascript nevertheless lets you, because, well, javascript. When you do silly things in javascript, javascript will give you silly answers, and in that way javascript is rather worthless - programmers doing crazy stuff that cannot reasonably be interpreted as having any particular meaning should be answered with a clear error and not a wild stab in the dark. But that's just how javascript is. The usual way to deal with this crazy behaviour is to never ask javascript silly things, as it will give you silly answers. Such as 1<<32 being 1*.
You may be wondering 'but how is asking to bit shift 1 by 53 positions 'crazy'? - and the answer is, that bit shifts, given that they make no sense on doubles, are interpreted as: "You wish to emulate 32-bit signed int behaviour", and that is exactly what javascript does, notably including the weirdish java/C-ism that only the bottom 5 bits of the number on the RHS count. In other words, <<32 is the same thing as <<0 - after all, the bottom 5 bits of 32.. is 0. Said differently, take the right hand side number, divide it by 32, toss the result, keep the remainder ('modulo'). 32 divided by 32 leaves a remainder of 0. 53 divided by 32 leaves a remainder of 21, and that's why 1<<53 in javascript prints 2097152.
So, in javascript your code is effectively doing the double multiplied by 2 to the 21st power, or theDouble * 2097152, whereas in java it is doing the double multiplied by 2 to the 53rd power, or theDouble * 9007199254740992.
Rather obviously then your answers are wildly different.
The fix seems trivial. 1<<53 may look like a nice way to convey the notion of 2 to 53rd power or in bits, a 1 bit, followed by 53 zeroes, but as syntax goes it just does not work that way in javascript. You can't use that syntax for this purpose. Try literally 9007199254740992.
var d = 0.2515933907977884;
const n = d * 9007199254740992;
n
> 2266151802091599
so that works.
If you have a need to derive the value 9007199254740992 from the value 53:
Math.pow(2, 53)
> 9007199254740992
note that you're dancing on the edge of disaster here. standard IEEE doubles use 53 bits for the exponent, so you're at the very edges. Soon you'll get into the territory where 'x + 1' is equal to 'x' because the gap between representable numbers is larger than 1. You'll need to get cracking on BigInt if you want to move away from the precipice.
*) It is specced behaviour. But surely you agree this is highly surprising. How many people do you know that just know off-hand that javascripts << is specced to convert to a 32-bit signed integer, take the RHS and modulo 32 it, and then operate, and then convert back to double afterwards?

Javascript's Shift right with zero-fill operator (>>>) yielding unexpected result

First, (-1 >>> 0) === (2**32 - 1) which I expect is due to adding a new zero to the left, thus converting the number into 33-bit number?
But, Why is (-1 >>> 32) === (2**32 - 1) as well, while I expect it (after shifting the 32-bit number 32 times and replacing the Most Significant Bits with zeros) to be 0.
Shouldn't it be equal ((-1 >>> 31) >>> 1) === 0? or Am I missing something?
When you execute (-1 >>> 0) you are executing an unsigned right shift. The unsigned here is key. Per the spec, the result of >>> is always unsigned. -1 is represented as the two's compliment of 1. This in binary is all 1s (In an 8 bit system it'd be 11111111).
So now you are making it unsigned by executing >>> 0. You are saying, "shift the binary representation of -1, which is all 1s, by zero bits (make no changes), but make it return an unsigned number.” So, you get the value of all 1s. Go to any javascript console in a browser and type:
console.log(2**32 - 1) //4294967295
// 0b means binary representation, and it can have a negative sign
console.log(0b11111111111111111111111111111111) //4294967295
console.log(-0b1 >>> 0) //4294967295
Remember 2 ** any number minus 1 is always all ones in binary. It's the same number of ones as the power you raised two to. So 2**32 - 1 is 32 1s. For example, two to the 3rd power (eight) minus one (seven) is 111 in binary.
So for the next one (-1 >>> 32) === (2**32 - 1).... let's look at a few things. We know the binary representation of -1 is all 1s. Then shift it right one digit and you get the same value as having all 1s but precede it with a zero (and return an unsigned number).
console.log(-1 >>> 1) //2147483647
console.log(0b01111111111111111111111111111111) //2147483647
And keep shifting until you have 31 zeros and a single 1 at the end.
console.log(-1 >>> 31) //1
This makes sense to me, we have 31 0s and a single 1 now for our 32 bits.
So then you hit the weird case, shifting one more time should make zero right?
Per the spec:
6.1.6.1.11 Number::unsignedRightShift ( x, y )
Let lnum be ! ToInt32(x).
Let rnum be ! ToUint32(y).
Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.
Return the result of performing a zero-filling right shift of lnum by shiftCount bits. Vacated bits are filled with zero. The result is an unsigned 32-bit integer.
So we know we already have -1, which is all 1s in twos compliment. And we are going to shift it per the last step of the docs by shiftCount bits (which we think is 32). And shiftCount is:
Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.
So what is rnum & 0x1F? Well & means a bitwise AND operation. lnum is the number left of the >>> and rnum is the number right of it. So we are saying 32 AND 0x1F. Remember 32 is 100000. 0x is hexadecimal where each character can be represented by 4 bits. 1 is 0001 and F is 1111. So 0x1F is 00011111 or 11111 (31 in base 10, 2**5 - 1 also).
console.log(0x1F) //31 (which is 11111)
32: 100000 &
0x1F: 011111
---------
000000
The number of bits to shift if zero. This is because the leading 1 in 32 is not part of the 5 most significant bits! 32 is six bits. So we take 32 1s and shift it zero bits! That's why. The answer is still 32 1s.
On the example -1 >>> 31 this made sense because 31 is <= 5 bits. So we did
31: 11111 &
0x1F: 11111
-------
11111
And shifted it 31 bits.... as expected.
Let's test this further.... let's do
console.log(-1 >>> 33) //2147483647
console.log(-1 >>> 1) //2147483647
That makes sense, just shift it one bit.
33: 100001 &
0x1F: 011111
---------
00001
So, go over 5 bits with a bitwise operator and get confused. Want to play stump the dummy with a person who hasn't researched the ECMAScript to answer a stackoverflow post? Just ask why are these the same.
console.log(-1 >>> 24033) //2147483647
console.log(-1 >>> 1) //2147483647
Well of course it's because
console.log(0b101110111100001) // 24033
console.log(0b000000000000001) // 1
// ^^^^^ I only care about these bits!!!
When you do (-1 >>> 0), you are turning the sign bit into zero while keeping the rest of the number the same, therefore ending up as 2**32 - 1.
The next behaviour is documented in the ECMAScript specification. The actual number of shifts is going to be "the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F".
Since 32 & 0x1F === 0, both of your results will be identical.

Getting least significant bit in JavaScript

I am trying to get the least significant bit of a number in JavaScript.
I have the following code:
let lsb = (parseInt("110", 2) & 0xffff);
By my understanding, the least significant bit of 110 is 110 as it is the right-most set bit.
However, the code above returns '6', which is the total value of 110 and not the least significant bit.
How can I get the least significant bit?
I take you at your example that you are looking for the lowest set bit, not the least significant bit
What you're looking for is a bit of a bitwise hack.
We can do this with some exploitation of the way negative numbers are represented (two's complement)
var lowestSetBit = (value) & (-value)
If you are actually looking for the least significant bit, then you can just mask on that bit
var leastSignificantBit = value & 1
The least significant bit is the rightmost bit, not the rightmost bit that's set. To get that, AND with 1.
let lsb = parseInt("110", 2) & 1;
https://en.wikipedia.org/wiki/Least_significant_bit:
least significant bit (LSB) is the bit position in a binary integer
giving the units value, that is, determining whether the number is
even or odd
So it's easy:
let lsb = parseInt("110", 2) & 1
or even this:
let lsb = parseInt("110", 2) % 2
Finding the least significant bit of a number can easily be done by:
someNumber & 1
or in your specific case:
let lsb = (parseInt("110", 2) & 1
This works by masking every bit with a zero except for the least significant bit, which is &'d with that 1.
For example, let's have our input number be 21
21 & 1
Is the same as:
10101
& 00001
-------
00001 // => returns 1 since the last bit is turned on

Math.random and arithmetic shift

If I have the following code in JavaScript:
var index1 = (Math.random() * 6) >> 0;
var index2 = Math.floor(Math.random() * 6);
The results for index1 or index2 are anywhere between 0 and 6.
I must be confused with my understanding of the >> operator. I thought that by using arithmetic shift that the results for index1 would be anywhere between 1 and 6.
I am noticing, however that I don't need to use Math.floor() or Math.round() for index1 if I use the >> operator.
I know I can achieve this by adding 1 to both indexes, but I was hoping there was a better way of ensuring results are from 1 to 6 instead of adding 1.
I'm aware that bitwise operators treat their operands as a sequence of 32 bits (zeros and ones), rather than as decimal, hexadecimal, or octal numbers. For example, the decimal number nine has a binary representation of 1001. Bitwise operators perform their operations on such binary representations, but they return standard JavaScript numerical values.
UPDATE:
I saw the original usage in this CAAT tutorial on line 26 and was wondering whether that would actually return a random number between 1 and 6 and it seems it would only ever return a random number between 0 and 6. So you would never actually see the anim1.png fish image!
Thank you in advance for any enlightenment.
Wikipedia says '(Arithmetic right shifts for negative numbers would be equivalent to division using rounding towards 0 in one's complement representation of signed numbers as was used by some historic computers.)'
Not exactly an answer, but the idea is that >> 0 is really specific and shouldn't be used in general for getting a range between 1 and 6.
Most people would tell you to do
Math.floor((Math.random()*10)+1);

Using bitwise operators in javascript

I am creating a bitmask in javascript. It works fine for bit 0 through 14. When I set only bit fifteen to 1. It yields the integer value of "-2147483648" instead of "2147483648". I can do a special case hack here by returning hardcoded "2147483648" for bit fifteen but I would like to know the correct way of doing it.
Sample code:
function join_bitmap(hex_lower_word, hex_upper_word)
{
var lower_word = parseInt(hex_lower_word, 16);
var upper_word = parseInt(hex_upper_word, 16);
return (0x00000000ffffffff & ((upper_word<<16) | lower_word));
}
Above code returns -2147483648 when hex_lower_word is "0x0" and hex_upper_word is "0x8000" instead of 2147483648
The reason for this is because Javascript's bit shift operations use signed 32-bit integers. So if you do this:
0x1 << 31 // sets the 15th bit of the high word
It will set the sign bit to 1, which means negative.
On the other hand instead of bit shifting you multiply by powers of two, you'll get the result you want:
1 * Math.pow(2, 31)
The reason is, you are setting the sign bit...
2147483648 is 1 followed by 31 zeros in binary...
As you are doing a bitwise operation, the output is always a signed 32 bit number, which makes the 32nd bit the sign bit, so you get a negative number...
Update
(upper_word * Math.pow(2, 16))
will give positive 2147483648.
But, you still have the OR operation, which puts us back to square one...
As previous answers explained, the bitwise operators are 32 bit signed. Thus, if at any point along the way you set bit 31, things will go badly wrong.
In your code, the expression
(upper_word<<16) | lower_word)
is evaluated first because of the parentheses, and since upper_word has the top bit set, you will now have a negative number (0x80000000 = -2147483648)
The solution is to make sure that you do not shift a 1into bit 31 - so you have to set bit 15 of the upper word to zero before shifting:
mask15 = 0x7fff;
((upper_word&mask15)<<16|lower_word)
This will take care of "numbers that are too big become negative", but it won't solve the problem completely - it will just give the wrong answer! To get back to the right answer, you need to set bit 31 in the answer, iff bit 15 was set in upper_word:
bit15 = 0x8000;
bit31 = 0x80000000;
answer = answer + (upper_word & bit15)?bit31:0;
The rewritten function then becomes:
function join_bitmap(hex_lower_word, hex_upper_word)
{
var lower_word = parseInt(hex_lower_word, 16);
var upper_word = parseInt(hex_upper_word, 16);
var mask15 = 0x7fff;
var bit15 = 0x8000;
var bit31 = 0x80000000;
return 0xffffffff & (((upper_word&mask15)<<16) | lower_word) + ((upper_word & bit15)?bit31:0);
}
There isn't just a single "hard coded special case" - there are 2 billion or so. This takes care of all of them.

Categories