views:

109

answers:

4

Is there is a simple, pythonic way of rounding to the nearest whole number without using floating point? I'd like to do the following but with integer arithmetic:

skip = int(round(1.0 * total / surplus))

==============

@John: Floating point is not reproducible across platforms. If you want your code to pass tests across different platforms then you need to avoid floating point (or add some hacky espilon stuff to your tests and hope it works). The above may be simple enough that it would be the same on most/all platforms, but I'd rather not make that determination as it is easier to avoid floating point altogether. How is that "not in the spirit of Python"?

+6  A: 
skip = (((total << 1) // surplus) + 1) >> 1

Shifting things left by one bit effectively multiplies by two, shifting things right by one bit divides by two rounding down. Adding one in the middle makes it so that "rounding down" is actually rounding up if the result would have been above a .5 decimal part.

It's basically the same as if you wrote...

skip = int((1.0*total/surplus) + 0.5)

except with everything multplied by 2, and then later divided by 2, which is something you can do with integer arithmetic (since bit shifts don't require floating point).

Amber
Right idea, but I think the quantity you need to add to `total` is commensurate to `surplus`. I would replace the "+ 1" by "+ surplus" in your current formula and that would probably be about right.
Pascal Cuoq
Actually I just need to move the 1 outside. :) It's equivalent to adding surplus inside, but requires fewer lookups.
Amber
@Amber yes, that's another possibility.
Pascal Cuoq
Thanks! It is not immediately obvious to me that this works correctly in all the edge cases, and I'll check to be sure.
Jeff
I'd recommend multiplying multiplying by two to multiply by two, and dividing by two to divide by two, unless this is actually profiled, performance-sensitive code.
Glenn Maynard
A: 

This should work too:

def rint(n):
    return (int(n+.5) if n > 0 else int(n-.5))
rubik
A: 

Simply take care of the rounding rule before you ever divide. For the simplest round-half-up:

if total % surplus < surplus / 2:
    return total / surplus
else:
    return (total / surplus) + 1

Tweak a little bit if you need to do a proper round-to-even.

hobbs
+4  A: 

You can do this quite simply:

(n + d // 2) // d, where n is the dividend and d is the divisor.

Alternatives like (((n << 1) // d) + 1) >> 1 or the equivalent (((n * 2) // d) + 1) // 2 may be SLOWER in recent CPythons, where an int is implemented like the old long.

The simple method does 3 variable accesses, 1 constant load, and 3 integer operations. The complicated methods do 2 variable accesses, 3 constant loads, and 4 integer operations. Integer operations are likely to take time which depends on the sizes of the numbers involved. Variable accesses of function locals don't involve "lookups".

If you are really desparate for speed, do benchmarks. Otherwise, KISS.

John Machin
+1 This method is both more readable than the bit-shifting approach, and also faster (in timeit testing) on Py 2.7.
ma3