Tuesday, March 22, 2016

Caution JavaScript Ahead - toString(2) is not returning right output always

Introduction

There is Number.toString() method in JavaScript. Passing 2 to this function convert number to binary string. But is it returning right value always?

Background

This require electronics or computer science background as its talking more about internal of computing machine such as 2's complement. But people who do programming without that background can also have a try reading this article.

Converting positive numbers toString(2)

Look at the below code which convert number to string by passing 2 as argument to get binary string representation.

var binary= (6).toString(2); 
alert(binary);

Output
110

This works perfect.

Converting negative numbers toString(2)

Lets convert a negative number to binary using the same technique.

var binary= (-6).toString(2); 
alert(binary);

Output
-110

This looks simple as we are getting the binary digits which is equal to absolute value(value without sign) of number. This is easy to understand for a person who don't have computer science of electronics background. But are we getting right binary representation of -6? No. Its wrong.

2's complement representation

In computer, it stores the data including numbers in binary as it knows only 2 states. On or Off or 1 or 0 respectively. We can store signed numbers as well as unsigned numbers. For signed numbers it require one bit to denote the sign. If the number variable has maximum 4 bits to store, it can store up to -8 to +7 in case, it needs to support -ve numbers. If it is unsigned data type, it can store from 0-15 ie 0 to 24 -1.

Another point to note is the storage mechanism of -ve numbers. One bit is not just kept for sign. Instead it stores as 2's complement. It does so to do add operation easily. Lets see how its represented by taking 4 bit number data type for easy understanding

6 - 0110 - a positive number
-6 - 1010 - Representing -6 in 2's complement

But look at the out put which showed -110 earlier for -6. Ideally the output should be the 2's complement (1010) based on the data type length.

Finding 2's complement

The steps are simple. Find the 1's complement by inverting bits. Then add 1 to the result. Lets see how -5 can be written in 2's complement way. Here first write the binary representation of 5 without sign.

0101 - Leading 0 is required as the data type length we took here is 4.
1010 - Just replace 0s with 1s and 1s with 0s.This is 2s complement.
1011 - Added 1 to get 2s compliment.

Hope its clear and we can easily calculate it for any number. There are online tools available to convert. Try those to learn faster.

Trouble of showing (-binaryNumber) in toString(2)

Normally there is no problem when the negative number is converted as shown in the above sample. -110 for -6 instead of 1010. But it gets complicated when we deal with bit-wise operations such as shifting bits in -ve number and convert to string. Look at the below snippet.


var binary= (6>>1).toString(2); 
alert(binary);
binary= (-6>>1).toString(2); 
alert(binary);

Output - 
11
-11

This gives an impression that the number is just converted to binary regardless of sign and shift the bits right once.

00000000 00000000 00000000 00000110 - Binary of 6
00000000 00000000 00000000 00000011 - After shifting bits one time to right

Added zeros as JavaScript does bit wise operations using 32 bit.

Now check the below example

var binary= (7>>1).toString(2); 
console.log(binary);
binary= (-7>>1).toString(2); 
console.log(binary);

Output - 
11
-100

We could see that the +ve number 7 is simply shifted right and -7 is not. This is because we are expecting just -11 which is nothing but adding -ve sign to  11. 

00000000 00000000 00000000 00000111 - Binary of 6
00000000 00000000 00000000 00000011 - After shifting bits one time to right

We may think that -100 is wrong only because we have understanding that the computer is handling -7 as just 7 with a sign in front of it. In reality, it stores 2's complement which is not what we are seeing when we displayed it using toString(2). Lets see how we got -100

11111111 11111111 11111111 11111001 - 2's complement of -7
11111111 11111111 11111111 11111100 - After shifting bits one time to right

Finding the number from 2's complement 

Its the simple 'reverse process in order' what we did for finding 2's complement.
11111111 11111111 11111111 11111011 - Subtracted one
00000000 00000000 00000000 00000100 - Inverted bits (replace 0s with 1s and viceversa)

This is why we are seeing -100 when we converted to binary using toString(2) after shifting. Try how its working for -6 to get same -11 for +3 and -3.

Conclusion

Number.toString(2) is not showing the 2's complement representation of number which is used internally. But the bit wise operations work on 2's complement.

If we don't understand the 2's complement and we do shifting business with -ve numbers thinking they just have additional sign. Sometime we will see its working (see the case of +6 & -6). But it may fail for other numbers.

Points of Interest

If (-7).toString(2) returned 11111111 11111111 11111111 11111001 it would have been easier to understand how the numbers are stored inside computers. But in the other hand this makes it difficult to understand for common people. Now it is not advisable to change the behavior as there are so many apps written on the current working of toString(2). Those may fail because of correction. May be another overload will help.

There are improvement done in JavaScript which does in new ways to fix issues in old implementation. == and === is the famous example.

No comments: