The code is ~9x faster than Math.floor(). Replacing the doubles with floats makes it faster, but the results are rather... random, so don't.

public class FastMath { private static final int BIG_ENOUGH_INT = 16 * 1024; private static final double BIG_ENOUGH_FLOOR = BIG_ENOUGH_INT + 0.0000; private static final double BIG_ENOUGH_ROUND = BIG_ENOUGH_INT + 0.5000; private static final double BIG_ENOUGH_CEIL = BIG_ENOUGH_INT + 0.9999; public static int fastFloor(float x) { return (int) (x + BIG_ENOUGH_FLOOR) - BIG_ENOUGH_INT; } public static int fastRound(float x) { return (int) (x + BIG_ENOUGH_ROUND) - BIG_ENOUGH_INT; } public static int fastCeil(float x) { return (int) (x + BIG_ENOUGH_CEIL) - BIG_ENOUGH_INT; } }

Clever code, very useful. Thanks for posting.

ReplyDeleteKeith

I'll append to this as I found this to be the fastest version overall. I was using doubles so the speed improvements are different, but I tried 4 different approaches.

ReplyDeletedouble val

Standard Floor: Math.floor(val)

Bound checking: val < 0 ? ((int)val) - 1 : (int)val

Adding/Subtracting (Riven method): ((int)(val + x)) - x

Raw casting (flawed): (int)val

The results, running many loops and first burning it in before timing so it's not a hotspot artifact.

Standard Floor: 30.41 seconds

Bound checking: 14.26 seconds

Adding/Subtracting: 13.24 seconds

Raw casting (flawed): 9.02 seconds

If you're pulling the values from arrays and storing them in arrays(my case), the adding subtracting performs slightly better than the bound checking as you don't have to create a temporary variable/make multiple array access.

(Array Access times)

Standard Floor: 37.46 seconds

Bound checking: 23.01 seconds

Adding/Subtracting: 17.63 seconds

Raw casting (flawed): 12.33 seconds