By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
 435,639 Members | 2,262 Online
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,639 IT Pros & Developers. It's quick & easy.

Math.ceil/floor Vs parseInt Vs plus/minus

 P: n/a I am writing a script to move an absolutely positioned element on a page by a factor using style.top & style.left. The amount to move by is always some fraction, so I was tossing up between Math.ceil/floor and parseInt +/- 1 to ensure the amount to move was at least 1 and in the right direction. I made a small test to see which one is faster and also included simply adding/subtracting 1. parseInt generally took 50% to 100% longer than Math.ceil/floor, but adding/subtracting 1 is 30 to 100 times faster (depending on the browser) than Math.ceil/floor. Naturally I'd like to use that since it's so much quicker. So my question is: Is it safe to set the 'top' or 'left' of an element to some fractional measurement of pixels? e.g. is it safe to do: x = 1.9863098 el.style.left = x + 'px' or should I be making x an integer first (using parseInt or Math.ceil/floor)? Here's my test script:
Number of iterations
-- Rob Jul 23 '05 #1
Share this Question
6 Replies

 P: n/a RobG wrote: I am writing a script to move an absolutely positioned element on a page by a factor using style.top & style.left. The amount to move by is always some fraction, so I was tossing up between Math.ceil/floor and parseInt +/- 1 to ensure the amount to move was at least 1 and in the right direction. I made a small test to see which one is faster and also included simply adding/subtracting 1. parseInt generally took 50% to 100% longer than Math.ceil/floor, It would as parseInt type-converts non-string arguments into strings prior to parsing them. but adding/subtracting 1 is 30 to 100 times faster (depending on the browser) Irrelevant if you have to do that anyway. than Math.ceil/floor. Naturally I'd like to use that since it's so much quicker. Subtraction is no substitute for rounding. So my question is: Is it safe to set the 'top' or 'left' of an element to some fractional measurement of pixels? e.g. is it safe to do: x = 1.9863098 el.style.left = x + 'px' No. Many browsers will happily make pixel approximations of non-integer values (they need to in order to cope with em, %, etc. units), but some go belly up. It is much safer only to ask for pixel positions in integers. or should I be making x an integer first (using parseInt or Math.ceil/floor)? No. If you want to turn a number with a fractional part into an integer suitable for use in positioning in the quickest way possible use:- x | 0 - or:- x >> 0 - as the bitwise operations truncate their operands to 32 bit signed integer values (except for - >>> -, which uses ToUint32 internally instead of ToInt32). 32 bit signed integers are sufficiently big to cove all realistic pixel positioning and dimension requirements. Generally, if you want fast math for pixel positioning work try to use bitwise operators, particularly at the end of sequences of calculations. For example, halving a dimension can be done as - ( x >> 1 ) - and be guaranteed to return an integer result (so will not then need to be floored), so:- ((totalWidth - elementWidth) >> 1) - gives you a guaranteed integer result that is no bigger than half of the width not taken up by elementWidth, ideal for centring an element in a space, and without the need for further rounding. i = this.form.num.value; s = new Date(); while ( i-- ) { ( x>0 )? parseInt(x)+1 : parseInt(x)-1; It is difficult for me to tell what you are after from this. My inclination would be to handle distance and direction independently. Acquiring a +1/-1 value for direction and a separate positive value for distance. That allows the distance to be rounded in a known directions (ie, truncated with bitwise operators, floored or ceiled) and then direction applied by multiplication with +1/-1 (preserving the integer nature of the result). Many expressions can be used to give +1/-1 results based on Math.random(), E.G.:- Math.random()<0.5 ? +1 : -1 // fastest on average, by a // small margin (((Math.random()<0.5)&& +1)||-1) ((Math.random()+0.5)<<1)-1 ((Math.random()<0.5)<<1)-1 So given a positive distance - d - that starts truncated/floored to an integer, but must be made at least 1, and a direction - v -, that will be either -1 or +1, you might have:- (d + !d) * v - where if - d - is non-zero - !d - is false, adding zero after type-conversion to a number, and when - d - is zero - !d is true, adding 1 following type-conversion. Then the dimensions - d -, truncated/floored and adjusted by one,if needed, only in one direction, is multiplied by +1 or -1 to give an integer distance in one of two opposite directions. Richard. Jul 23 '05 #2

 P: n/a Richard Cornford wrote: RobG wrote: [...] Irrelevant if you have to do that anyway. The strategy is to emulate ceil/floor, i.e. ensure that +ve numbers are always 'rounded' up and -ve always 'rounded' down. This can be done by adding 1 to +ve and subtracting 1 from -ve and truncating. The trick is to do the logic for determining +ve/-ve, add/subtract and truncate with the quickest algorithm. I want numbers like 0.1 to become 1 (add 1 and truncate or use ceil) and -0.1 to become -1 (subtract 1 and truncate or use floor). It seems simply adding/subtracting and letting the browser truncate when setting style.left/top is not reliable - small 'jitters' occur randomly near the end of the movement, different browsers have different behaviours. I was pretty sure I'd be told to pass integers. than Math.ceil/floor. Naturally I'd like to use that sinceit's so much quicker. Subtraction is no substitute for rounding. In the context described above, it kinda works, but not reliably, so I guess you're right. [...] No. Many browsers will happily make pixel approximations of non-integer values (they need to in order to cope with em, %, etc. units), but some go belly up. It is much safer only to ask for pixel positions in integers. Agreed, and the results are inconsistent across browsers so it's a poor strategy - unless unpredictable jitters with imprecise movement are intended. :-) or should I be making x an integer first (usingparseInt or Math.ceil/floor)? No. If you want to turn a number with a fractional part into an integer suitable for use in positioning in the quickest way possible use:- x | 0 - or:- x >> 0 - as the bitwise operations truncate their operands to 32 bit signed integer values (except for - >>> -, which uses ToUint32 internally instead of ToInt32). 32 bit signed integers are sufficiently big to cove all realistic pixel positioning and dimension requirements. Here's my final version. moveBy is generated by some function of the distance between two elements: if ( moveBy ) { setPos( obj, ((moveBy > 0)? ( moveBy+=1 | 0 ) : ( moveBy-=1 | 0 ) )); } It's neatly symmetrical, fast and +ve/-ve movement and truncating are dealt with in one go. Which is at least as fast as simply adding/subtracting one and is faster by a factor of about 15 than the equivalent: setPos( obj, ((moveBy > 0)? Math.ceil(moveBy) : Math.floor(moveBy))); Generally, if you want fast math for pixel positioning work try to use bitwise operators, particularly at the end of sequences of calculations. For example, halving a dimension can be done as - ( x >> 1 ) - and be guaranteed to return an integer result (so will not then need to be floored), so:- ((totalWidth - elementWidth) >> 1) - gives you a guaranteed integer result that is no bigger than half of the width not taken up by elementWidth, ideal for centring an element in a space, and without the need for further rounding. Yes, but I need to account for direction (+ve -ve) and would like to have that done within a single step as above. i = this.form.num.value; s = new Date(); while ( i-- ) { ( x>0 )? parseInt(x)+1 : parseInt(x)-1; It is difficult for me to tell what you are after from this. My It's just a test of part of my logic, on its own it's pretty meaningless. ;-p [...] Many expressions can be used to give +1/-1 results based on Math.random(), E.G.:- Math.random()<0.5 ? +1 : -1 // fastest on average, by a // small margin (((Math.random()<0.5)&& +1)||-1) ((Math.random()+0.5)<<1)-1 ((Math.random()<0.5)<<1)-1 So given a positive distance - d - that starts truncated/floored to an integer, but must be made at least 1, and a direction - v -, that will be either -1 or +1, you might have:- (d + !d) * v - where if - d - is non-zero - !d - is false, adding zero after type-conversion to a number, and when - d - is zero - !d is true, adding 1 following type-conversion. Then the dimensions - d -, truncated/floored and adjusted by one,if needed, only in one direction, is multiplied by +1 or -1 to give an integer distance in one of two opposite directions. Actually I'm trying to avoid random movement (!) but thanks for the tip, I'm sure it will come in handy. -- Rob Jul 23 '05 #3

 P: n/a RobG wrote: Here's my final version. moveBy is generated by some function of the distance between two elements: if ( moveBy ) { setPos( obj, ((moveBy > 0)? ( moveBy+=1 | 0 ) : ( moveBy-=1 | 0 ) )); } In looking at this I observe that the value that is determined by the outcome of the conditional expression is the +/-1, so maybe;- setPos( obj, ((moveBy += ((moveBy > 0)?1:-1)) | 0) ) Richard. Jul 23 '05 #4

 P: n/a Richard Cornford wrote: RobG wrote: Here's my final version. moveBy is generated by somefunction of the distance between two elements:if ( moveBy ) { setPos( obj, ((moveBy > 0)? ( moveBy+=1 | 0 ) : ( moveBy-=1 | 0 ))); } In looking at this I observe that the value that is determined by the outcome of the conditional expression is the +/-1, so maybe;- setPos( obj, ((moveBy += ((moveBy > 0)?1:-1)) | 0) ) And the outer bit-wise truncates - sweet! -- Rob Jul 23 '05 #5

 P: n/a JRS: In article , dated Fri, 8 Jul 2005 01:13:11, seen in news:comp.lang.javascript, RobG posted :Richard Cornford wrote: RobG wrote: Here's my final version. moveBy is generated by somefunction of the distance between two elements:if ( moveBy ) { setPos( obj, ((moveBy > 0)? ( moveBy+=1 | 0 ) : ( moveBy-=1 | 0 ))); } In looking at this I observe that the value that is determined by the outcome of the conditional expression is the +/-1, so maybe;- setPos( obj, ((moveBy += ((moveBy > 0)?1:-1)) | 0) )And the outer bit-wise truncates - sweet! If you are repeatedly moving in the same direction by function(distance), then the direction should be determined, for speed, outside the move loop. Possibly, write a function to move +, another to move -, and at the start of each sequence assign one or other to do the job; but function calls may be slower. If you are using new Date() to time your moves, you probably need not care much about the speed of the move calculation code, depending on OS; unless the OS maintains and serves the date as a day- or seconds- count, there's more work in new Date() than there can be in move-calculation. And, between ticks, one can calculate a lot. Timing with setTimeout or setInterval would not have those overheads, but different ones. -- © John Stockton, Surrey, UK. ?@merlyn.demon.co.uk Turnpike v4.00 IE 4 © JL/RC: FAQ of news:comp.lang.javascript jscr maths, dates, sources. TP/BP/Delphi/jscr/&c, FAQ items, links. Jul 23 '05 #6

 P: n/a Dr John Stockton wrote: JRS: In article , dated Fri, 8 Jul 2005 01:13:11, seen in news:comp.lang.javascript, RobG posted :Richard Cornford wrote:RobG wrote:Here's my final version. moveBy is generated by somefunction of the distance between two elements:if ( moveBy ) { setPos( obj, ((moveBy > 0)? ( moveBy+=1 | 0 ) : ( moveBy-=1 | 0 ))); }In looking at this I observe that the value that is determined by theoutcome of the conditional expression is the +/-1, so maybe;-setPos( obj, ((moveBy += ((moveBy > 0)?1:-1)) | 0) )And the outer bit-wise truncates - sweet! If you are repeatedly moving in the same direction by function(distance), then the direction should be determined, for speed, outside the move loop. Possibly, write a function to move +, another to move -, and at the start of each sequence assign one or other to do the job; but function calls may be slower. Thanks, I'll consider that. The destination can move before it is reached, therefore position and distance are calculated each time after a move. I am working on creating objects that learn to move of their own accord - accelerate, move with a set speed, then decelerate. But they also need to be updated with where the destination has moved to as they go. My biggest speed issue is eliminating the use of Math functions. They are handy to use to get the logic right, but then optimisation is required - using a function that is 30 times faster frees-up quite a bit of CPU! If you are using new Date() to time your moves, you probably need not care much about the speed of the move calculation code, depending on OS; unless the OS maintains and serves the date as a day- or seconds- count, there's more work in new Date() than there can be in move-calculation. And, between ticks, one can calculate a lot. I was using a date object to time sections of code - usually 10,000 or so loops with multiple runs are required to get meaningful results. I've also found that when testing a series loops in a single function, going first is a distinct disadvantage. Timing with setTimeout or setInterval would not have those overheads, but different ones. -- Rob Jul 23 '05 #7

This discussion thread is closed

Replies have been disabled for this discussion.