52

I'm working on a page that processes IP address information, but it's choking on the fact that integers are signed. I am using bitwise operators to speed it up, but the 64th bit (signed/unsigned flag) is messing it up.

Is there any way to force a number to be unsigned in Javascript? It seems to work fine, until subnet is greater than 30, or less than 2.

Try this:

<html>
    <body>
    
    <script type='text/javascript'>
    document.write( (1 << 30) +"<br/>");
    document.write( (1 << 31) +"<br/>");
    document.write( (1 << 32) +"<br/>");
    </script>
    
    </body>
</html>

Result:

1073741824 -2147483648 1

Community
  • 1
  • 1
bradlis7
  • 3,375
  • 3
  • 26
  • 34
  • 3
    You should probably process the different parts of the IP address as several numbers, to avoid overflow issues. – Eric Bréchemier Dec 15 '09 at 16:12
  • 3
    1<<32 results in a 33-bit number. That wouldn't fit in a C/C++ unsigned int either. The other two results are correct. – moonshadow Dec 15 '09 at 16:26
  • @Ates Goral - It is simple when compared to C++. I'm mainly speaking about the fact that types do not exist in JS. Maybe instead of "simple", I mean "less powerful". – bradlis7 Dec 15 '09 at 17:47
  • I think that using individual bits is the wrong way to go. It's seldom justifiable in C (except for enums), and I'm quite sure _never_ in Javascript or other higher level languages. – Georg Schölly Dec 15 '09 at 19:50
  • I'm hosting this program at http://tools.bradlis7.com/ip/ for now. – bradlis7 Jan 11 '10 at 19:28
  • Excellent question (and solution), imho. Using bitwise ops to turn an ip tuple into an int is the most intuitive method I can think of. And in Big Data apps (like my own), probably the fastest by far. – Chuck Jan 16 '12 at 19:00
  • @moonshadow `int` and `unsigned int` can have as little as 16 bits, and as much as the implementer wants in C. Usually they are as wide as the integer registers, from 16 to 64 bits. – curiousguy Mar 08 '16 at 21:51

7 Answers7

97
document.write( (1 << 31) +"<br/>");

The << operator is defined as working on signed 32-bit integers (converted from the native Number storage of double-precision float). So 1<<31 must result in a negative number.

The only JavaScript operator that works using unsigned 32-bit integers is >>>. You can exploit this to convert a signed-integer-in-Number you've been working on with the other bitwise operators to an unsigned-integer-in-Number:

document.write(( (1<<31)>>>0 )+'<br />');

Meanwhile:

document.write( (1 << 32) +"<br/>");

won't work because all shift operations use only the lowest 5 bits of shift (in JavaScript and other C-like languages too). <<32 is equal to <<0, ie. no change.

bobince
  • 528,062
  • 107
  • 651
  • 834
  • 2
    +1: Very informative. I was wondering why the `1 << 32` resulted in a 1 and not 0. – Joel Dec 15 '09 at 16:34
  • Good information. It's too bad Javascript is so limited, it's so much easier for me to design interfaces in HTML. They should add PyScript as a language in Firefox :). – bradlis7 Dec 15 '09 at 16:39
  • Niggle: `1<<31` is `0x80000000` which is `INT_MIN`, or -2147483648. -1 is `0xFFFFFFFF`. – fluffy Jan 29 '15 at 19:21
  • What's the deal with this? `(((1<<31)>>>0) * 2)>>>0` returns `0` and not `1` – Chet Aug 05 '17 at 19:05
  • 1
    Why would it ever return 1? You're multiplying 2^31 by 2, resulting in 2^32, then convert it to 32-bit number, which results in 0. – riv Jun 12 '18 at 14:25
12

Douglas Crockford believes that bitwise operators is one of the bad parts of javascript:

In Java, the bitwise operators work with integers. JavaScript doesn't have integers. It only has double precision floating-point numbers. So, the bitwise operators convert their number operands into integers, do their business, and then convert them back. In most languages, these operators are very close to the hardware and very fast. In JavaScript, they are very far from the hardware and very slow. JavaScript is rarely used for doing bit manipulation.

-- Douglas Crockford in "JavaScript: The Good Parts", Appendix B, Bitwise Operators (emphasis added)

Are you sure that bitwise operators really speed up your logic?

Community
  • 1
  • 1
Artem Latyshev
  • 575
  • 3
  • 9
  • That's true. Honestly, I have not done much programming outside of high-level languages. I just assumed that doing `1 << 24` would be faster than `Math.pow(2,24)` in processing speed. I have changed all of my code to use `pow`, and it seems to be just as fast. – bradlis7 Dec 17 '09 at 15:55
  • bitwise in Firefox is super uber fast compared to all other methods. in chrome is almost the same as normal operations. (FF4 vs Chrome 12) – vsync Jun 23 '11 at 17:25
  • 1
    Maybe modern browsers can optimize bitwise (as well as Math.pow) to be nearer to hardware. If not today, then in future. – Timo Kähkönen Nov 21 '12 at 10:30
  • 1
    Yes, because that's what emscripten and other to JS compilers have been using and in response the JS engines have been optimizing for it. – Indolering Dec 07 '13 at 05:43
  • As of 2009 (i.e. the year this answer was posted) version 1.2 of the V8 engine started to support smi tagging, which allowed numbers to be stored as 32 bit integers unless some modification required the use of doubles. As of 2021, even 64 bit smi tags are supported. It has been safe to assume for a long time now that any browser your JS is running in will do smi tagging. – Adam Leggett Apr 02 '23 at 17:47
11

Use >>> instead of >> to get an unsigned right shift instead of a sign-extending one. All the other bitwise operators behave the same way regardless of whether ints are signed or not.

Your code breaking "when subnet ... is less than 2" is concerning. That sounds like you may have some bug unrelated to signedness of integers.

moonshadow
  • 86,889
  • 7
  • 82
  • 122
  • The problem is not the subnet, is that I compute both the Number of Networks, and Number of Hosts. One is going to be 2^31 (which is 1 << 31) if n = 1. – bradlis7 Dec 15 '09 at 16:31
  • And what's wrong with that? 1<<31 gives you the correct value - namely, it has bit 31 set. Bitwise logic - masks, shifts etc - will behave as you expect. Now, if you actually want to *display* a value greater than ((1<<31)-1) in the form of an integer in the range 2147483647..4294967296, you're going to have to do some nasty thing or other, e.g. alert(x<0?4294967296+x:x) which works by coercing x back to the double representation internally. – moonshadow Dec 15 '09 at 16:45
6

Javascript doesn't have integers, all numbers are actually doubles.

The Javascript 1.5 Reference by Mozilla suggests that one can only use bitwise-operations safely for 32 bit numbers.

Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
  • 2
    I read somewhere that when you do bitwise operations, it's converted to an integer, and back to the given type (double, I would assume). – bradlis7 Dec 15 '09 at 16:11
  • You know, I've looked at that page 100 times, and didn't see "32-bit". Maybe I'll go back to using Math.pow(2,n), instead of (1 << n). It'll take a few more processing loops in theory, but at least it will work. – bradlis7 Dec 15 '09 at 16:33
  • Math.pow won't help you if you use the shift or bitwise boolean operators on the result as it'll be coerced to a 32-bit signed integer; e.g. alert(Math.pow(2,31)|1) yields -2147483647 – moonshadow Dec 15 '09 at 16:49
  • @moonshadow, that's because, you put an | in the equation. I changed them all to using Math.pow, and it works fine. – bradlis7 Dec 15 '09 at 18:18
  • @bradlis7 if you look at a given bitwise operator such as & from that page it will mention 32-bit integers. – Dan S Feb 07 '21 at 18:35
4

Here are two functions that convert ipv4 addresses to/from unsigned integers in javascript:

function ip2long(ip) {
    var ipl=0;
    ip.split('.').forEach(function( octet ) {
        ipl<<=8;
        ipl+=parseInt(octet);
    });
    return(ipl >>>0);
}

function long2ip (ipl) {
    return ( (ipl>>>24) +'.' +
        (ipl>>16 & 255) +'.' +
        (ipl>>8 & 255) +'.' +
        (ipl & 255) );
}
0

What kind of IP addresses do you have? IPv4 uses only 32bit addresses, so JavaScript should be fine (using double which gives you an 52bit integer part). IPv6 uses 128bit addresses, so you'll have to use an array. My guess is that something else is broken.

[EDIT] Build a small library which uses an array of two ints as the internal data type.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • 1
    I thought about a library, but it's too much effort unless it's the only option. You're right about 32/64-bit, but the bitwise operators work only on 32-bit integers for some reason. – bradlis7 Dec 15 '09 at 16:42
0

Javascript now has a bigint type, you can use the n suffix to make such a number.

So using your example you could do:

<body>
    
    <script type='text/javascript'>
    document.write( (1n << 30n) +"<br/>");
    document.write( (1n << 31n) +"<br/>");
    document.write( (1n << 32n) +"<br/>");
    </script>
    
    </body>
</html>
Thayne
  • 6,619
  • 2
  • 42
  • 67