What is the tilde doing in this line of javascript? - javascript

I am trying to understand this line of code. What is the minus and tilde doing to r[e]?:
r = {}
for (e of s)
r[e] = -~r[e] // What is this specific line assigning?
for (e in r)
if (r[e] == 1)
return e
return '_'
The problem this code solves is this(specific line is commented):
Given a string s, find and return the first instance of a
non-repeating character in it. If there is no such character, return
'_'.
I understand the other lines except the commented one.

Tilde is a unary operator that takes the expression to its right performs this small algorithm on it
-(N+1) // N is the expression right to the tilde
So in your code it is incrementing r[e] with 1 (because of double negation).
See the examples below:
console.log(~-2); // 1
console.log(~-1); // 0
console.log(~0); // -1
console.log(~1); // -2
console.log(~2); // -3

Tilde is the bitwise operator for NOT operation.
It takes in a number as operand, converts it into a 32 bit integer (refer IEEE_754-1985 and flips all the bits. 0 becomes 1, 1 becomes 0.
For example, if the number 5 is represented by
00000000 00000000 00000000 00000101
The number ~5 is the above, with the bits flipped
11111111 11111111 11111111 11111010
The decimal value of ~5 is (-6). This is also known as 2's complement. Since the most significant bits, which represent the sign of the number in JavaScript are flipped, the sign will always change. 2's complement causes the value of X to change to -(X+1)
Certain applications like engines, use bitwise data structures and bitwise operations play a role in those.
Bitwise OR (|)
Bitwise AND (&)
Bitwise NOT (~)
Bitwise XOR (^)
Bitwise LEFT SHIFT (<<)
Bitwise RIGHT SHIFT (>>)

Related

I need an explanation (and possible workaround) for ((2^32-1) << 0) resulting -1 in Javascript

EDIT: Explanation at the end.
I was trying to implement a 64bit integer class using a Uint32Array and have bitwise operations performed under the hood on two uint32 members. I quickly found out that, as to my understanding of the specification, bitwise operations return a signed 32bit integer. Initially I was hoping that the Uint32Array would just take care of the sign bit, but it doesn't.
I tried coding around the sign issue, but I am stuck at something I simply can't make sense of at all.
var a = (Math.pow(2, 32)-1); //set a to uint32 max value
So far, so good.
a.toString(2);// gives "11111111111111111111111111111111", as expected
However:
(a << 0); // gives "-1"
(a >> 1); // gives "-1"
(a << 0) == (a >> 1); // evaluates to true
Even if JS bitwise operations turn numbers into signed 32bit integers, 32 set bits shifted to the right by 1 should never be -1. Or should they? Should a non-zero number shifted by 0 bits equal itself shifted 1 bit? Is this a bug? Am I running into undefined behaviour?
Usually the answer to similar questions has to do with the signed 32bit conversion but I can't see how that should cause this behaviour.
EDIT2, explanation: The cause of my confusion was a fundamental misunderstanding of how negative numbers are represented in binary. While the first bit is in fact the sign bit, 1 indicating a negative, 0 a positive number, the remaining bits aren't just used to store the abs(), as I assumed.
Signed 4bit example:
0111 equals +7. 1111 does not equal -7, it equals -1. How do we end up with negative one? Because the two's complement of 1111 is 0001. To get a number's two's complement, flip all bits and add one:
1111 -> 0000 -> 0001.
Now that I know that, making sense of 11..11 << 0 being -1 is easy. It's perfectly similar to my 4bit example. 11..11 >> 1 being -1 is also completely expected now. The signed right shift >> is 1 filling, so 11..11 >> 1 is still 11..11 which is still -1.
I will leave this as is for now, because I'm certainly not the only one misunderstanding binary signed integer representation. Thanks for everyone's time.
Even if JS bitwise operations turn numbers into signed 32bit integers, 32 set bits shifted to the right by 1 should never be -1. Or should they? Should a non-zero number shifted by 0 bits equal itself shifted 1 bit? Is this a bug? Am I running into undefined behaviour?
That's normal, expected and defined. And yes, they should.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Right_shift is what you use, and its description is this:
The right shift operator (>>) shifts the first operand the specified number of bits to the right. Excess bits shifted off to the right are discarded. Copies of the leftmost bit are shifted in from the left. Since the new leftmost bit has the same value as the previous leftmost bit, the sign bit (the leftmost bit) does not change. Hence the name "sign-propagating".
So if you have 32 bits of 1, after applying right shift by 1 you will have 32 bits of 1.
The fact that it's 32 bits wide is in the specs, https://tc39.es/ecma262/
6.1.6.1.10 Number::signedRightShift ( x, y )
[...]
4. Return the result of performing a sign-extending right shift of lnum by shiftCount bits. The most significant bit is propagated. The result is a signed 32-bit integer.
(Similarly, << produces 32-bit signed integer)

How does | and + do the trick to turn string into number?

When I was reading a doc about Symbol on MDN, I noticed these things can trun string into number which I've never seen before.
Quote:
When trying to convert a symbol to a number, a TypeError will be
thrown (e.g. +sym or sym | 0).
For example:
+"15"
will return
15
which is number type.
Also
"15" | 0
can do the same thing.
I am wondering how does this trick work.
Can you help?
+"15" is casting the "15" to a number type, the same way -15 works.
eg.
>> -"15" === -15
>> true
The second case, "15" | 0 is doing the same thing, casting to an integer in order to perform a Bitwise OR.
Which means taking the bits of 15 and ORing them with the bits of zero.
15 in binary is, for example 00001111 and zero is 00000000 so each bit is or'd with each other resulting in 15 again which is returned.
Unary Plus Operator
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Unary_plus_()
The unary plus operator precedes its operand and evaluates to its operand but attempts to convert it into a number, if it isn't already. Although unary negation (-) also can convert non-numbers, unary plus is the fastest and preferred way of converting something into a number, because it does not perform any other operations on the number. It can convert string representations of integers and floats, as well as the non-string values true, false, and null. Integers in both decimal and hexadecimal ("0x"-prefixed) formats are supported. Negative numbers are supported (though not for hex). If it cannot parse a particular value, it will evaluate to NaN.
+"6";//6
+6;//6
+-6;//-6
+undefined;//NaN
+false;//0
+true;//1
+null;//0
+{};//NaN
+[];//0
+function(){};//NaN
Bitwise OR Operator
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#.7c_%28Bitwise_OR%29
The operands are converted to 32-bit integers and expressed by a series of bits (zeroes and ones). Numbers with more than 32 bits get their most significant bits discarded.
Each bit in the first operand is paired with the corresponding bit in the second operand: first bit to first bit, second bit to second bit, and so on.
The operator is applied to each pair of bits, and the result is constructed bitwise.
The Bitwise OR Operator first converts both operands to 32-bit integers and each bit is compared. When comparing the two bits, if any of the bits is 1, 1 is returned. If both bits are 0, 0 is returned.
Example:
2|1;//produces 3
--------
00000010 //2 in binary
00000001 //1 in binary
--------
00000011 //3 in binary

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'

Bitwise operations with big integers

I am implementing decoding of BER-compressed integers and recently I've found a weird JavaScript behavior related to bitwise operations with big integers.
E.g.:
var a = 17516032; // has 25 bits
alert(a << 7) // outputs -2052915200
alert(a * 128) // outputs 2242052096
alert(2242052096 >> 16) // outputs -31325
alert(2242052096 / 65536) // outputs 34211
While the first workaround (multiplication instead of left shift) is acceptable, the second isn't.
Why it happens? How to bear with it?
17516032 in binary is 00000001000010110100011000000000. Shifting to the left by 7 gives you 10000101101000110000000000000000. This is equal to -2052915200 in two's complement (which is how almost all computers represent negative numbers).
>> is a signed right shift. That means that the leftmost bit (which determines the sign of a number) will be shifted into the left side.
e.g.
1100 >> 2 == 1111
0111 >> 2 == 0001
If you want to do an unsigned shift (which ignores the sign bit), use >>> which will zero-fill the left end of the bitstring.
Bitwise operators work on 32 bit integers, while multiplication and division works on floating point numbers.
When you shift a number, it's converted from a floating point number to a 32 bit integer before the operations, and converted back to a floating point number after the operation. The number 2242052096 has the 32nd bit set, so it is a negative number when converted to and from a 32 bit integer.
The >> right shift operator doesn't change the sign of the value, i.e. the bits that are shifted in from the left have the same value as the sign bit. Use the >>> right shift operator to shift in zero bits instead.
Reference: MDN: Bitwise operators
(2242052096 / 65536) == (2242052096 >>> 16)
Note the different shift.
Javascript normally represents numbers as (double-precision) floating point.
Almost all bitwise operations convert to a signed 32-bit integer, do whatever they're going to do, then treat the result as a signed 32-bit integer when converting back.
The exception is >>> which treats the result as an unsigned 32-bit integer when converting back.
So:
right shifts can be made to work simply by using >>> instead of >> ;
a * 128 gives the expected answer because it's never converted to a signed 32-bit integer in the first place - it's just a floating-point multiplication;
a << 7 gives an unexpected answer because it's converted to a signed 32-bit integer, and then you shift a 1 into the sign bit, resulting in a negative signed 32-bit value.
There isn't a <<<, but if you want to write your left shift as a shift, you can use
(a << 7) >>> 0
to get the expected answer (the >>> 0 effectively casts the signed 32-bit value to an unsigned 32-bit value).

Double more-than symbol in JavaScript

What does this: >> mean in JavaScript?
Seen in this context:
document.onkeydown = document.onkeyup = function(e,v,y,k) {
(i=e.keyCode-37)>>2 || (keys[i] = e.type[5]&&1||(0))
}
>> is the bitwise right shift operator.
For example: 4 >> 1 equals 2 because 4 is 100 in binary notation, which is shifted one bit to the right, giving us 10 = 2
Javascript Bitwise Operators
Left shift a << b Shifts a in binary
representation b (< 32) bits to the
left, shifting in zeros from the
right.
Sign-propagating right shift a >>
b Shifts a in binary representation b
(< 32) bits to the right, discarding
bits shifted off.
(i=e.keyCode-37)>>2
This code is discarding the two least significant bits of i (similar to dividing by 4), and comparing the result to zero. This will be false when the key pressed is 37-40 (arrow keys), and true otherwise.
It's the Bitwise shift operator (see here).
Now, as to exactly what it's doing here I'm not sure... I'm sure some of our larger-brained bretheren that actually finished college could help us out with that. ;^)

Categories

Resources