tags:

views:

93

answers:

5
var1=anyInteger
var2=anyInteger

(Math.round(var1/var2)*var2)

What would be the syntax for JavaScripts bitshift alternative for the above?

Using integer not floating

Thank you

A: 

Unfortunately, bit shifting operations usually only work with integers. Are your variables integers or floats?

benjismith
JavaScript numbers are 64-bit floating point numbers.
el.pescado
The pn and sn are simply variables for integers. Could just as well be (Math.round(2/4)*4). Thank you
cube
A: 

Does this page help? http://javascript.about.com/library/blbitop.htm

RueTheWhirled
+2  A: 

If var2 is a power of two (2^k) you may write

(var1>>k)<<k

but in the general case there is no straightforward solution.

ragnarius
+2  A: 

You can do (var | 0) - that would truncate the number to an integer, but you'll always get the floor value. If you want to round it, you'll need an additional if statement, but in this case Math.round would be faster anyway.

casablanca
+1  A: 

[UPDATED] The quick answer:

var intResult = ((((var1 / var2) + 0.5) << 1) >> 1) * var2;

It's faster than the Math.round() method provided in the question and provides the exact same values.

Bit-shifting is between 10 and 20% faster from my tests. Below is some updated code that compares the two methods.

The code below has four parts: first, it creates 10,000 sets of two random integers; second, it does the round in the OP's question, stores the value for later comparison and logs the total time of execution; third, it does an equivalent bit-shift, stored the value for later comparison, and logs the execution time; fourth, it compares the Round and Bit-shift values to find any differences. It should report no anomalies.

Note that this should work for all positive, non-zero values. If the code encounters a zero for the denominator, it will raise and error, and I'm pretty sure that negative values will not bit-shift correctly, though I've not tested.

var arr1 = [],
    arr2 = [],
    arrFloorValues = [],
    arrShiftValues = [],
    intFloorTime = 0,
    intShiftTime = 0,
    mathround = Math.round, // @trinithis's excellent suggestion
    i;

// Step one: create random values to compare
for (i = 0; i < 100000; i++) {
    arr1.push(Math.round(Math.random() * 1000) + 1);
    arr2.push(Math.round(Math.random() * 1000) + 1);
}

// Step two: test speed of Math.round()
var intStartTime = new Date().getTime();
for (i = 0; i < arr1.length; i++) {
    arrFloorValues.push(mathround(arr1[i] / arr2[i]) * arr2[i]);
}
console.log("Math.floor(): " + (new Date().getTime() - intStartTime));

// Step three: test speed of bit shift
var intStartTime = new Date().getTime();
for (i = 0; i < arr1.length; i++) {
    arrShiftValues.push( ( ( ( (arr1[i] / arr2[i]) + 0.5) << 1 ) >> 1 ) * arr2[i]);

}
console.log("Shifting: " + (new Date().getTime() - intStartTime));

// Step four: confirm that Math.round() and bit-shift produce same values
intMaxAsserts = 100;
for (i = 0; i < arr1.length; i++) {
    if (arrShiftValues[i] !== arrFloorValues[i]) {
        console.log("failed on",arr1[i],arr2[i],arrFloorValues[i],arrShiftValues[i])
        if (intMaxAsserts-- < 0) break;
    }
}
Andrew
I don't want to check, but perhaps keeping a direct reference to `Math.round`, say `var round = Math.round;` would give a more accurate result.
trinithis
True. It does shave about 2% off the execution time on Firefox and Chrome. In the latest Safari (which is abou 5x faster), the difference is negligible. I do think that there is a problem with my bit-shifting math though. I'm working to correct that.
Andrew
Adding 0.5 does apparently leave the same value as Math.round(). Updating answer. I've tested for only positive integers and I think this will not work with any integer less than 1.
Andrew
Thank you Andrew, and trinithis as well. Andrew, I have Google'd for days trying to find any kind of write up, blog, or anything, on making a more efficient rounding method. I could find not one single note on the subject. You are brilliant! Thanks again!
cube
Very happy to help. It was an interesting problem! I also learned something from this. Back in primary school, I learned that decimal numbers that lay exactly between two whole numbers are supposed to round to the nearest even number (i.e. 1.5 -> 2, 2.5 -> 2,3.5 -> 4,etc.). In JS, these numbers always round up (towards positive infinity). That tidbit simplified the logic a lot!
Andrew
@Andrew - Wow, you learned "banker's rounding" in primary school. I learned the simple round-up method and had never even heard of banker's rounding until after college.
benjismith