Understanding bitwise operations in javascript - javascript

I am currently storing data inside an XML doc as binary, 20 digits long, each representing a boolean value.
<matrix>
<resource type="single">
<map>10001010100011110000</map>
<name>Resource Title</name>
<url>http://www.yoursite.com</url>
</resource>
</matrix>
I am parsing this with jQuery and am currently using a for loop and charAt() to determine whether to do stuff if the value is == "1".
for (var i = 0; i < _mapLength; i++) {
if (map.charAt(i) == "1") {
//perform something here
}
}
This takes place a few times as part of a HUGE loop that has run sort of slow. Someone told me that I should use bitwise operators to process this and it would run faster.
My question is either:
Can someone offer me an example of how I could do this? I've tried to read tutorials online and they seem to be flying right over my head. (FYI: I am planning on creating a Ruby script that will convert my binary 0 & 1's into bits in my XML.)
Or does anyone know of a good, simple (maybe even dumbed down version) tutorial or something that could help me grasp these bitwise operator concepts?

Assuming you have no more than 32 bits, you can use JavaScript's built-in parseInt() function to convert your string of 1s and 0s into an integer, and then test the flags using the & (and) operator:
var flags = parseInt("10001010100011110000", 2); // base 2
if ( flags & 0x1 )
{
// do something
}
...
See also: How to check my byte flag?
(question is on the use in C, but applies to the same operators in JS as well)

Single ampersand (&, as opposed to &&) does bit-wise comparison. But first you need to convert your strings to numbers using parseInt().
var map = parseInt("10010", 2); // the 2 tells it to treat the string as binary
var maskForOperation1 = parseInt("10000", 2);
var maskForOperation2 = parseInt("01000", 2);
// ...
if (map & maskForOperation1) { Operation1(); }
if (map & maskForOperation2) { Operation2(); }
// ...

Be extremely wary. Javascript does not have integers -- numbers are stored as 64 bit floating-point. You should get accurate conversion out to 52 bits. If you get more flags than that, bad things will happen as your "number" gets rounded to the nearest representable floating-point number. (ouch!)
Also, bitwise manipulation will not help performance, because the floating point number will be converted to an integer, tested, and then converted back.
If you have several places that you want to check the flags, I'd set the flags on an object, preferably with names, like so:
var flags = {};
flags.use_apples = map.charAt(4);
flags.use_bananas = map.charAt(10);
etc...
Then you can test those flags inside your loop:
if(flags.use_apples) {
do_apple_thing();
}
An object slot test will be faster than a bitwise check, since Javascript is not optimized for bitwise operators. However, if your loop is slow, I fear that decoding these flags is probably not the source of the slowness.

Bitwise operators will certainly be faster but only linearly and not by much. You'll probably save a few milliseconds (unless you're processing HUGE amounts of data in Javascript, which is most likely a bad idea anyway).
You should think about profiling other code in your loop to see what's slowing it down the most. What other algorithms, data structures and allocations do you have in there that could use refactoring?

Related

How v8 Javascript engine performs bit operations on Int32Array values

As far as I know, V8 Javascript engine makes double number conversion (to i32 and back) to perform bit operation.
Let's consider next example:
const int32 = new Int32Array(2);
int32[0] = 42;
int32[1] = 2;
const y = int32[0] | int32[1];
Does V8 makes double conversion in this case ?
If no, does it mean bit operations are faster on Int32Array values?
UPDATE:
By double conversion I mean:
from 64 bit (53 bits precision) to -> 32bit and again to -> 64 bit
V8 developer here. The short answer is: there are no conversions here, bitwise operations are always performed on 32-bit integers, and an Int32Array also stores its elements as 32-bit integers.
The longer answer is "it depends". In unoptimized code, all values use a unified representation. In V8, that means number values are either represented as a "Smi" ("small integer") if they are in range (31 bits including sign, i.e. about -1 billion to +1 billion), or as "HeapNumbers" (64-bit double with a small object header) otherwise. So for an element load like int32[0], the 32-bit value is loaded from the array, inspected for range, and then either tagged as a Smi or boxed as a HeapNumber. The following | operation looks at the input, converts it to a 32-bit integer (which could be as simple as untagging a Smi, or as complex as invoking .valueOf on an object), and performs the bitwise "or". The result is then again either tagged as a Smi or boxed as a HeapNumber, so that the next operation can decide what to do with it.
If the function runs often enough to eventually get optimized, then the optimizing compiler makes use of type feedback (guarded by type checks where necessary) to elide all these conversions that are unnecessary in this case, and the simple answer will be true.

Issue with combining large array of numbers into one single number

I am trying to convert an array of numbers into one single number, for example
[1,2,3] to 123.
However, my code can't handle big arrays since it can’t return exact number. Such as
[6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,5,4,3] returns 6145390195186705000
Is there any way that I could properly convert into a single number.I would really appreciate any help.
var integer = 0;
var digits = [1,2,3,4]
//combine array of digits into int
digits.forEach((num,index,self) => {
integer += num * Math.pow(10,self.length-index-1)
});
The biggest integer value javacript can hold is +/- 9007199254740991. Note that the bitwise operators and shift operators operate on 32-bit ints, so in that case, the max safe integer is 2^31-1, or 2147483647.
In my opinion, you can choose one of the following:
store the numbers as strings and manipulate them as numbers; you might have to implement special functions to add/subtract/multiply/divide them (these are classic algorithmic problems)
use the BigInt; BigInts are a new numeric primitive in JavaScript that can represent integers with arbitrary precision. With BigInts, you can safely store and operate on large integers even beyond the safe integer limit. Unfortunately, they work only with Chrome right now. If you want to work with other browsers, you might check this or even this if you work with angularjs or nodejs.
Try the following code in the Chrome's console:
let x = BigInt([6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5,5,4,3].join(''));
console.log(x);
This will print 6145390195186705543n. The n suffix marks that it is a big integer.
Cheers!
You can use JavaScript Array join() Method and parse it into integer.
Example:
parseInt([6,1,4,5,3,9,0,1,9,5,1,8,6,7,0,5].join(''))
results:
6145390195186705
Edited: Use BigInt instead of parseInt , but it works only on chrome browser.
The largest number possible in Javascript is
+/- 9007199254740991
Use BigInt. Join all numbers as a string and pass it in BigInt global function to convert it into int
var integer = 0;
var digits = [1,2,3,4]
//combine array of digits into int
digits.forEach((num,index,self) => {
integer += num;
});
integer= BigInt(integer);
Note : Works only on Chrome as of now. You can use othee libraries like BigInteger.js or MathJS

Javascript can't render this number correctly: 3494793310847464221

I have an interesting question, I have been doing some work with javascript and a database ID came out as "3494793310847464221", now this is being entered into javascript as a number yet it is using the number as a different value, both when output to an alert and when being passed to another javascript function.
Here is some example code to show the error to its fullest.
<html><head><script language="javascript">alert( 3494793310847464221);
var rar = 3494793310847464221;
alert(rar);
</script></head></html>
This has completly baffeled me and for once google is not my friend...
btw the number is 179 more then the number there...
Your number is larger than the maximum allowed integer value in javascript (2^53). This has previously been covered by What is JavaScript's highest integer value that a Number can go to without losing precision?
In JavaScript, all numbers (even integral ones) are stored as IEEE-754 floating-point numbers. However, FPs have limited "precision" (see the Wikipedia article for more info), so your number isn't able to be represented exactly.
You will need to either store your number as a string or use some other "bignum" approach (unfortunately, I don't know of any JS bignum libraries off the top of my head).
Edit: After doing a little digging, it doesn't seem as if there's been a lot of work done in the way of JavaScript bignum libraries. In fact, the only bignum implementation of any kind that I was able to find is Edward Martin's JavaScript High Precision Calculator.
Use a string instead.
179 more is one way to look at it. Another way is, after the first 16 digits, any further digit is 0. I don't know the details, but it looks like your variable only stores up to 16 digits.
That number exceeds (2^31)-1, and that's the problem; javascript uses 32-bit signed integers (meaning, a range from –2,147,483,648 to 2,147,483,647). Your best choice is to use strings, and create functions to manipulate the strings as numbers.
I wouldn't be all too surprised, if there already was a library that does what you need.
One possible solution is to use a BigInt library such as: http://www.leemon.com/crypto/BigInt.html
This will allow you to store integers of arbitrary precision, but it will not be as fast as standard arithmetic.
Since it's to big to be stored as int, it's converted to float. In JavaScript ther is no explicit integer and float types, there's only universal Number type.
"Can't increment and decrement a string easily..."
Really?
function incr_num(x) {
var lastdigit=Number(x.charAt(x.length-1));
if (lastdigit!=9) return (x.substring(0,x.length-1))+""+(lastdigit+1);
if (x=="9") return "10";
return incr_num(x.substring(0,x.length-1))+"0";
}
function decr_num(x) {
if(x=="0") return "(error: cannot decrement zero)";
var lastdigit=Number(x.charAt(x.length-1));
if (lastdigit!=0) return (x.substring(0,x.length-1))+""+(lastdigit-1);
if (x=="10") return "9"; // delete this line if you like leading zero
return decr_num(x.substring(0,x.length-1))+"9";
}
Just guessing, but perhaps the number is stored as a floating type, and the difference might be because of some rounding error. If that is the case it might work correctly if you use another interpreter (browser, or whatever you are running it in)

parseInt alternative

Firstly - my description ;)
I've got a XmlHttpRequests JSON response from the server.
MySQL driver outputs all data as string and PHP returns it as it is, so any integer is returned as string, therefore:
Is there any fast alternative (hack) for parseInt() function in JS which can parse pure numeric string, e.g.
var foo = {"bar": "123"};
...
foo.bar = parseInt(foo.bar); // (int) 123
To convert to an integer simply use the unary + operator, it should be the fastest way:
var int = +string;
Conversions to other types can be done in a similar manner:
var string = otherType + "";
var bool = !!anything;
More info.
Type casting in JavaScript is done via the constructor functions of the built-in types without new, ie
foo.bar = Number(foo.bar);
This differs from parseInt() in several ways:
leading zeros won't trigger octal mode
floating point values will be parsed as well
the whole string is parsed, i.e. if it contains additional non-numeric characters, the return value will be NaN
First off, have you actually documented that it's slow and is causing problems? Otherwise, I wouldn't bother looking for a solution, because there really isn't a problem.
Secondly, I would guess that since parseInt is a native JS-method, it would be implemented in a way that is very fast, and probably in the native language of the VM (probably C, depending on the browser/VM). I think you could have some trouble making a faster method out of pure JS. =)
Of course, I'm not a JS guru, so I don't know for sure, but this is what my intuition tells me, and tends to be the standard answer to "how would I make a faster alternative for libraryFunction()?" questions.
Cast it to an int in PHP before you json_encode() it:
$foo->bar = (int)$foo->bar;
print('var foo = ' . json_encode($foo));
Incidentally, when using parseInt it's good practice to always specify the second parameter unless you really want string starting with 0 to be interpreted as octal and so on:
parseInt('010', 10); // 10
Fast shortcut to parseInt is
("78.5" | 0) //bitwise or forces the string to parse as int
This is what ASM uses to represent ints in js.
You aren't going to get better than parseInt, but the real bug is that the PHP is providing what is supposed to be a number as a string.
And ditto to what Daniel said - don't go looking for micro-optimisations like this until you have benchmarked your code and discovered that it's worth doing.
The Number constructor also exists, but it should be the same as parseInt in term of speed (as already said you should correct the PHP part instead of the javascript one anyway) :
var i = "123";
i = new Number(i); // Number numeric wrapper
var j = "123";
j = Number(j); // Number primitive
BTW if someone is interested i searched by curiosity for the V8 (Google chrome) implementation of parseInt and it's here on google code.
How slow can it be? How many times per second is this process being called? How many different numeric return values are there? I whipped together a script and tested 100,000 numbers. Parsing them from strings took 687ms. Searching them in an array took 541ms. That's a very small improvement. I agree with other posters. You may not get better than the native parseInt() method.
Casting is a wee bit faster than parsing but slower than searching.
Also, in Firefox the fastest method turns out to be parseInt() followed by searching. Firefox also turned out to be 6 times faster on average than IE. Interesting.
Cool idea using the unary operator. In Firefox that turned out to be comparable to parseInt(). In IE it turned out to be the fastest method.
if the objects are larger you could try JSON, it is a typed format so you do not need to convert the values.
This solution is faster than parseInt() if you parse strings of decimal integer that is 20 or less in length. For some browser, you may still be faster than parseInt() up to 33 digits in length. Also, you still be faster than auto-cast.
It is because, the parseInt() for the browser does take some time to warm up, so if you only using a simple method to parse, you beat it for a while until it catches up. Don't use this for nodeJS though. When run parseInt() from nodeJS, it is startup time is a lot less than when running from a browser.
45 is the '-' sign in ASCII, 43 is the '+' sign in ASCII. 48 is '0'. Only 48 to 57 xor 48 become 0 - 9(in their order). No other numbers xor 48 yields 0-9.
This will return undefined if the string is not a valid decimal integer string or if the string is empty. It throws a string with value "Not a string" if the input is not of type string.
var toNumber = function (input) {
if ( typeof input !== "string" ) throw "Not a string";
var length = input.length;
if ( length === 0 ) return;
var c1 = input.charCodeAt(0);
if ( c1 === 45 || c1 === 43 ){
if ( length === 1 ) return;
var start = 1;
} else {
var start = 0;
}
var out = 0, c;
while( start < length && input.charCodeAt(start) === 48 ) start++;
for ( ; start < length; start++){
c = input.charCodeAt(start) ^ 48;
if ( c > 9 ) return;
out = (out * 10) + c;
}
if ( c1 === 45 ) return out * -1;
return out;
}

What is the accepted way to send 64-bit values over JSON?

Some of my data are 64-bit integers. I would like to send these to a JavaScript program running on a page.
However, as far as I can tell, integers in most JavaScript implementations are 32-bit signed quantities.
My two options seem to be:
Send the values as strings
Send the values as 64-bit floating point numbers
Option (1) isn't perfect, but option (2) seems far less perfect (loss of data).
How have you handled this situation?
There is in fact a limitation at JavaScript/ECMAScript level of precision to 53-bit for integers (they are stored in the mantissa of a "double-like" 8 bytes memory buffer). So transmitting big numbers as JSON won't be unserialized as expected by the JavaScript client, which would truncate them to its 53-bit resolution.
> parseInt("10765432100123456789")
10765432100123458000
See the Number.MAX_SAFE_INTEGER constant and Number.isSafeInteger() function:
The MAX_SAFE_INTEGER constant has a value of 9007199254740991. The
reasoning behind that number is that JavaScript uses double-precision
floating-point format numbers as specified in IEEE 754 and can only
safely represent numbers between -(2^53 - 1) and 2^53 - 1.
Safe in this context refers to the ability to represent integers
exactly and to correctly compare them. For example,
Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 will
evaluate to true, which is mathematically incorrect. See
Number.isSafeInteger() for more information.
Due to the resolution of floats in JavaScript, using "64-bit floating point numbers" as you proposed would suffer from the very same restriction.
IMHO the best option is to transmit such values as text. It would be still perfectly readable JSON content, and would be easy do work with at JavaScript level.
A "pure string" representation is what OData specifies, for its Edm.Int64 or Edm.Decimal types.
What the Twitter API does in this case, is to add a specific ".._str": field in the JSON, as such:
{
"id": 10765432100123456789, // for JSON compliant clients
"id_str": "10765432100123456789", // for JavaScript
...
}
I like this option very much, since it would be still compatible with int64 capable clients. In practice, such duplicated content in the JSON won't hurt much, if it is deflated/gzipped at HTTP level.
Once transmitted as string, you may use libraries like strint – a JavaScript library for string-encoded integers to handle such values.
Update: Newer versions of JavaScript engines include a BigInt object class, which is able to handle more than 53-bit. In fact, it can be used for arbitrarily large integers, so a good fit for 64-bit integer values. But when serializing as JSON, the BigInt value will be serialized as a JSON string - weirdly enough, but for compatibility purposes I guess.
This seems to be less a problem with JSON and more a problem with Javascript itself. What are you planning to do with these numbers? If it's just a magic token that you need to pass back to the website later on, by all means simply use a string containing the value. If you actually have to do arithmetic on the value, you could possibly write your own Javascript routines for 64-bit arithmetic.
One way that you could represent values in Javascript (and hence JSON) would be by splitting the numbers into two 32-bit values, eg.
[ 12345678, 12345678 ]
To split a 64-bit value into two 32-bit values, do something like this:
output_values[0] = (input_value >> 32) & 0xffffffff;
output_values[1] = input_value & 0xffffffff;
Then to recombine two 32-bit values to a 64-bit value:
input_value = ((int64_t) output_values[0]) << 32) | output_values[1];
Javascript's Number type (64 bit IEEE 754) only has about 53 bits of precision.
But, if you don't need to do any addition or multiplication, then you could keep 64-bit value as 4-character strings as JavaScript uses UTF-16.
For example, 1 could be encoded as "\u0000\u0000\u0000\u0001". This has the advantage that value comparison (==, >, <) works on strings as expected. It also seems straightforward to write bit operations:
function and64(a,b) {
var r = "";
for (var i = 0; i < 4; i++)
r += String.fromCharCode(a.charCodeAt(i) & b.charCodeAt(i));
return r;
}
The JS number representation is a standard ieee double, so you can't represent a 64 bit integer. iirc you get maybe 48 bits of actual int precision in a double, but all JS bitops reduce to 32bit precision (that's what the spec requires. yay!) so if you really need a 64bit int in js you'll need to implement your own 64 bit int logic library.
JSON itself doesn't care about implementation limits.
your problem is that JS can't handle your data, not the protocol.
In other words, your JS client code has to use either of those non-perfect options.
This thing happened to me. All hell broke loose when sending large integers via json into JSON.parse. I spent days trying to debug. Problem immediately solved when i transmitted the values as strings.
Use
{ "the_sequence_number": "20200707105904535" }
instead of
{ "the_sequence_number": 20200707105904535 }
To make it worse, it would seem that where every JSON.parse is implemented, is some shared lib between Firefox, Chrome and Opera because they all behaved exactly the same. Opera error messages have Chrome URL references in it, almost like WebKit shared by browsers.
console.log('event_listen[' + global_weird_counter + ']: to be sure, server responded with [' + aresponsetxt + ']');
var response = JSON.parse(aresponsetxt);
console.log('event_listen[' + global_weird_counter + ']: after json parse: ' + JSON.stringify(response));
The behaviour i got was the sort of stuff where pointer math went horribly bad. Ghosts were flying out of my workstation wreaking havoc in my sleep. They are all exorcised now that i switched to string.

Categories