PDA

View Full Version : >> syntax

Keleth
07-16-2012, 03:54 PM
So reading through a pluggin, I'm coming across this syntax:

var x = (mx - sImg.offset().left) >> 0

The math part I get fine, I'm more confused what the ">> 0" means, namely in terms of the assignment?

EDIT: I know >>/<< are used for bitwise operations, but I don't see how that makes sense here?

Logic Ali
07-16-2012, 04:52 PM
So reading through a pluggin, I'm coming across this syntax:

var x = (mx - sImg.offset().left) >> 0

The math part I get fine, I'm more confused what the ">> 0" means, namely in terms of the assignment?

EDIT: I know >>/<< are used for bitwise operations, but I don't see how that makes sense here?

An ineffective bit shift removes any decimal fraction, so it's probably intended as an equivalent to var x = Math.floor( mx - sImg.offset().left );

Keleth
07-16-2012, 05:13 PM
That's... weird...

Old Pedant
07-16-2012, 08:43 PM
Agreed. Anybody who uses crap like that ought to be shot. If you used stuff like that in a professional environment without *REALLY* good justification, the best that would happen to you would be tons of nasty remarks at code review time.

oneguy
07-16-2012, 10:12 PM
">> 0" is equivalent to Math.floor only for non-negative numbers less than 0x80000000.
">> 0" rounds the number towards zero and takes the number from the range from -0x80000000 to 0x7FFFFFFF which is equivalent modulo 0x100000000.
What is the best way to round a number towards zero if it's known the absolute value of the one is less than 0x80000000?

Old Pedant
07-16-2012, 10:28 PM
What is the best way to round a number towards zero if it's known the absolute value of the one is less than 0x80000000?

In other words, if you know the number is a 32 bit integer (but not 0x80000000) but you don't know if it is positive or negative?

Why not create a nice readable JS function?

function intValue( n )
{
var r = Math.floor( Math.abs( n ) ); // floor of the absolute value
return n < 0 ? -r : r; // recover the sign as needed
}

Best part: It even works for values OUTSIDE the normal range of integers!

e.g.,

document.write( intValue( -98765432188.888 ) );

Name it whatever you want to.

oneguy
07-16-2012, 10:36 PM
Why not create a nice readable JS function?

But ">>0" works much faster than your function and isn't less readable if you know what it does.

Old Pedant
07-16-2012, 10:46 PM
"much faster". Oh, yes. You will save 1 microsecond (MAYBE) each time it is used. On a client computer. [Okay, to be fair, it depends on how powerful the client computer is. But still...]

How many times do you expect to need to use this capability, per second, in your web page? Unless it's at least 1000 times per second I don't see how you would even be able to FIND the time difference.

*IF* you need to use this in some very very very FAST game, then maybe it would matter. I can't imagine in mattering in 98% of all web-based usages.

And if you can REALLY justify using "tricks" like >>0 because you are writing some very fast game, then you can probably also justify finding ways to ensure that you will never be using >>0 with a negative number (e.g., "bias" all your numbers enough that they are always positive?).

Old Pedant
07-16-2012, 11:00 PM
Here:

<script type="text/javascript">
var n = -738.781;
var t1, t2;

t1 = new Date();
for ( var i = 0; i < 10000000; ++i )
{
var r = Math.floor( Math.abs( n ) );
if ( n < 0 ) r = -r;
}
t2 = new Date();
document.write("using function: time was " + ( t2.getTime() - t1.getTime() ) + " ms.<hr>" );

t1 = new Date();
for ( var i = 0; i < 10000000; ++i )
{
var r = n >> 0;
}
t2 = new Date();
document.write("using >> only: time was " + ( t2.getTime() - t1.getTime() ) + " ms.<hr>" );

t1 = new Date();
for ( var i = 0; i < 10000000; ++i )
{
var r = n < 0 ? - ( -n >> 0 ) : n >> 0;
}
t2 = new Date();
document.write("using >> with sign check: time was " + ( t2.getTime() - t1.getTime() ) + " ms.<hr>" );
</script>

On my machine, an older Athlon, using Firefox, 10 million iterations you will notice:

The Math.floor(), etc., version seemed to actually take slightly LESS time (though barely so) than the ">> with sign check" version.

Both of them took only about 2.3 to 2.5 times longer than the bare >>0 alone.

In any case, worst case was about 100 milliseconds for 10 MILLION iterations. That's TEN NANOseconds per usage. Best case (for the >>0 with no check for negative) was around 40 milliseconds.

So, yes, each usage of the sign-checking versions costs you SIX NANOseconds or so.

Is it REALLY worth finding something faster?

Old Pedant
07-16-2012, 11:04 PM
WHOOPS!

Teach me to not do my benchmarking right.

If you really *DO* use a function call (my intValue of prior answer, for example), the times go WAY up for any of the answers.

Turns out the JS in Firefox is smart enough to realize that it is doing the same operation over and over, so it hoists the operation *OUT* of the loop. In other words, it optimizes my test out of existence.

I'll have to retry this with REAL examples.

oneguy
07-16-2012, 11:19 PM
Turns out the JS in Firefox is smart enough to realize that it is doing the same operation over and over, so it hoists the operation *OUT* of the loop.
I don't think so. This is because some operations are needed for a function call itself which take CPU time. The operation doesn't go out of loop because the time is proportional to the number of iterations.

Old Pedant
07-16-2012, 11:30 PM
Okay...let's try again:

<script type="text/javascript">
var nums = [
173.11, -718.88, 9.77, -1001881.11, 43.11, -53.88, 2222222.5, -111111.7, 33.3, -44.4
];

function intValue(num)
{
var r = Math.floor(Math.abs(num));
return num < 0 ? -r : r;
}

var t1, t2, sum;

t1 = new Date();
sum = 0;
for ( var i = 0; i < 1000000; ++i )
{
var n = nums[ i % 10 ];
sum += n;
}
t2 = new Date();
document.write("base loop time was " + ( t2.getTime() - t1.getTime() ) + " ms.<hr>" );

t1 = new Date();
sum = 0;
for ( var i = 0; i < 10000000; ++i )
{
var n = nums[ i % 10 ];
sum += n >> 0;
}
t2 = new Date();
document.write("using >>, time was " + ( t2.getTime() - t1.getTime() ) + " ms.<hr>" );

t1 = new Date();
sum = 0;
for ( var i = 0; i < 10000000; ++i )
{
var n = nums[ i % 10 ];
sum += n < 0 ? - ( -n >> 0 ) : n >> 0;
}
t2 = new Date();
document.write("using >> with time check, time was " + ( t2.getTime() - t1.getTime() ) + " ms.<hr>" );

t1 = new Date();
sum = 0;
for ( var i = 0; i < 10000000; ++i )
{
var n = nums[ i % 10 ];
var r = Math.floor( Math.abs(n) );
sum += n < 0 ? - r : r;
}
t2 = new Date();
document.write("using Math.floor, time was " + ( t2.getTime() - t1.getTime() ) + " ms.<hr>" );

t1 = new Date();
sum = 0;
for ( var i = 0; i < 10000000; ++i )
{
var n = nums[ i % 10 ];
sum += intValue(n);
}
t2 = new Date();
document.write("using function call, time was " + ( t2.getTime() - t1.getTime() ) + " ms.<hr>" );
</script>

A mildly surprising result:

base loop time was 29 ms
---------
using >>, time was 362 ms
---------
using >> with time check, time was 393 ms
---------
using Math.floor, time was 433 ms
---------
using function call, time was 5439 ms

So, from any of the other results, you should subtract the "base loop time" (as it represents the overhead of the loop, the array access, etc.).

Notice that the last two times are actually using the same basic code. But the difference is that the first one (433 ms) does it inline and the second one (5439 ms) does it by calling a user-defined function.

WOW! The *REAL* overhead here is obviously in simply the existence of the user-defined function. (I also tried creating a function that used the >>0 with sign check inside the function and it showed the same overhead.)

SO...the lesson learned here: If you need performance, write your JavaScript code inline and avoid non-native function calls. Because it is crystal clear, comparing the times for >>0 vs. Math.floor(Math.abs()) *inline*, that there is very little difference between the two. Clearly less than 10 nanoseconds per invocation.

Old Pedant
07-16-2012, 11:37 PM
I don't think so. This is because some operations are needed for a function call itself which take CPU time. The operation doesn't go out of loop because the time is proportional to the number of iterations.

I think I disagree, after seeing my revised benchmark results.

I know that good Java/C++/C# compilers can do this: The easily hoist non-variant operations out of loops and leave in the loop only the code that varies with each operation. I am not overly surprised that modern JavaScript compilers [and don't kid yourself: those *are* JS compilers operating under the covers] can do the same.

You are, of course, right about the function call taking time. [Note that good C++/Java/C# compilers would automatically "inline" any functions as simple as the ones I used, but clearly JavaScript doesn't--maybe can't?--do that.]

Anyway, it looks to me like >>0, with or without the sign check, and Math.floor(Math.abs()) with sign check all operate in between 30 and 50 nanoseconds on my machine. No matter how you cut it, that just isn't much time. And clearly you don't gain enough using >>0 to make it worth the difference in human readability. At least to me.

Richter
07-17-2012, 04:36 AM
Hi Keleth,
It's seem more alien syntax then you think :)
http://timmywillison.com/pres/operators/

oneguy
07-17-2012, 01:02 PM
Old Pedant, thank you for your tests. You agree that perfomance is an advantage of >>0 over using Math.floor with sign checking, don't you? But how valuable this advantage is, is another question. But I can't understand how this

var r = Math.floor( Math.abs(n) );
n < 0 ? - r : r;

can be more readable that this

n>>0

n is used twice instead of once, 2 line of code are used instead of 1, and a temporary variable is used. Furthermore, if we need to use an expression instead of n, which may be long or have side-effects, we need to use another temporary variable.
So, n>>0 is much nore readable for me.

Old Pedant
07-17-2012, 08:28 PM
Heh...good point. My argument was admittedly for using a function to do it. And then I proved that using a function was a performance hog.

Note that it *DOES* turn out that using >>0 vs. my two line (inline) code shows no significant difference in performance (on the order of 2 to 4 nanoseconds).

But, yes, using a function as I originally suggested costs 100 nanoseconds.

I still argue that for 98% of all JavaScript applications that you should, indeed, use a function for clarity's sake. But if you are indeed writing something for that 2% (or less) case, then it may be worth writing the more obscure code.

Note, though, that the question I was really answering was "how do I get a round-toward-zero function that works for both positive and negative numbers."

And in that case, the >>0 alone does *NOT* do the trick. You have to use

n < 0 ? - ( -n >> 0 ) : n >> 0
at a minimum. And, quite frankly, even if I used that code I would want to put it in a function, for clarity (with tons of comments about how and why and what it was doing).

oneguy
07-17-2012, 09:14 PM
And in that case, the >>0 alone does *NOT* do the trick.
Yes, it *DOES* :)

">> 0" rounds the number towards zero and takes the number from the range from -0x80000000 to 0x7FFFFFFF which is equivalent modulo 0x100000000.

You might have thought that -3.75>>0 returned -4. If so, you were incorrect, -3.75>>0 returns -3.

Old Pedant
07-17-2012, 11:04 PM
REALLY???

Wow.

See, in the HARDWARE, a shift right of zero bits is meaningless. Further, only integers (and only 32-bit integers if you want it to work on all CPUs) can be shifted.

So I just *assumed* (always a dangerous thing to do!) that when JS sees

someValue >> someBits

the first thing it does is convert someValue to a 32-bit integer. And, of course, I assumed that this conversion did the equivalent of Math.floor() [though presumably more efficiently]. But of course there was no reason for me to assume that. It's actually easier, at the machine code level, to do a bit-wise truncation. Which would, indeed, do as you say.

*SIGH* I was mislead by *YOUR* question in post #5:

What is the best way to round a number towards zero if it's known the absolute value of the one is less than 0x80000000?

Why in the world did you ask that question??

oneguy
07-17-2012, 11:46 PM
Why in the world did you ask that question??
Sorry, I didn't mean to mislead you. I saw that all were very surprised with using ">>0". So I wrote

">> 0" is equivalent to Math.floor only for non-negative numbers less than 0x80000000.
">> 0" rounds the number towards zero and takes the number from the range from -0x80000000 to 0x7FFFFFFF which is equivalent modulo 0x100000000.

to clarify things and when I asked

What is the best way to round a number towards zero if it's known the absolute value of the one is less than 0x80000000?

I implied that ">>0" is one way of doing this, and if there is no better way, we should approve of programmers writing such code.

Old Pedant
07-18-2012, 12:34 AM
Okay. I still wouldn't use it except in high-performance situations, and even then I would want to see it carefully commented. But clearly the rest of all the above was a waste of time due to my assuming what you were asking/saying.

felgall
07-18-2012, 04:42 AM
I think I disagree, after seeing my revised benchmark results.

Unlike most other programming languages where they are often the most efficient way to perform certain processes, the bit operators in JavaScript are the least efficient way to do anything and should be avoided 100% of the time.

rnd me
07-18-2012, 07:52 AM
Unlike most other programming languages where they are often the most efficient way to perform certain processes, the bit operators in JavaScript are the least efficient way to do anything and should be avoided 100% of the time.

not true.

all things being equal, they can be faster.

here's a quick test

//test function wrappers:
function shift(n){
return n >> 0;
}

function floor(n){
return Math.floor(n);
}

function parse(n){
return parseInt(n, 10);
}

function crop(n){
var t=(''+n), p;
return t.slice( 0, (p=t.indexOf("."))!==-1? p : 99 )*1;
}

var r=Array(99999).toString().split(",").map(Math.random);

[shift, floor, parse, crop]

.map(function(f){
var d=+new Date;
r.map(f);
return f.name+"\t" + (new Date - d);
}).join("\n");

it makes a big diff in JIT cores like ie9 and v8, as you can see below:

//firefox
shift 55
floor 64
parse 65
crop 180

//chrome:
shift 8
floor 41
parse 40
crop 128

//IE10:
undefined 3
undefined 11
undefined 10
undefined 111

when doing charting, animation, image or audio processing, it is worth the ugly imho.

test live link (http://danml.com/sandbox/#function%20shift%28n%29{%0A%20%20%20return%20n%20%3E%3E%200;%0A}%0A%0Afunction%20floor%28n%29{%0A%2 0%20%20return%20Math.floor%28n%29;%0A}%0A%0Afunction%20parse%28n%29{%0A%20%20%20return%20parseInt%28 n,%2010%29;%0A}%0A%0A%0Afunction%20crop%28n%29{%0A%20%20var%20t=%28%27%27+n%29,%20p;%0A%20%20%20retu rn%20t.slice%28%200,%20%28p=t.indexOf%28%22.%22%29%29!==-1?%20p%20:%2099%20%20%20%20%29*1;%0A}%0A%0A%0Avar%20r=Array%2899999%29.toString%28%29.split%28%22,%2 2%29.map%28Math.random%29;%0A%0A[shift,%20floor,%20parse,%20crop]%0A%0A.map%28function%28f%29{%0A%20%20%20var%20d=+new%20Date;%0A%20%20%20r.map%28f%29;%0A%20%20retur n%20f.name+%22\t%22%20+%20%28new%20Date%20-%20d%29;%0A}%29.join%28%22\n%22%29%0A%0A%0A%0A%0A)

Richter
07-18-2012, 12:14 PM
@rnd me

Opera 12

shift 17
floor 23
parse 166
crop 133

Crop faster then parse method, Opera !?

oneguy
07-18-2012, 02:47 PM
But clearly the rest of all the above was a waste of time due to my assuming what you were asking/saying.
I'm awfully sorry for wasting your time!

Old Pedant
07-18-2012, 09:28 PM
Oh, it was kind of fun, because I saw how really terrible JavaScript is with user-created functions. No excuse for that, really. Shows me there's still a lot of room for improvement in the compilers/JITs. I guarantee you that you'd never see anything close to that much of a performance penalty in C++, for example.

I once worked on a product that was, essentially, a JIT for both Java *AND* C++ (and allowed the two to interoperate very efficiently, by the by). And we pulled all sorts of tricks that I *KNOW* could be pulled with JavaScript JITs if the companies really wanted to. Granted, with JavaScript you have to be ready to throw away the JIT-ed code because of dynamic changes in the code, but even that is surely not even close to impossible. Kind of makes me wish we had tackled JavaScript instead of Java/C++. We might still be around.