Search code examples
mathlanguage-agnosticfloating-pointbit-manipulationinteger-arithmetic

Hacks for clamping integer to 0-255 and doubles to 0.0-1.0?


Are there any branch-less or similar hacks for clamping an integer to the interval of 0 to 255, or a double to the interval of 0.0 to 1.0? (Both ranges are meant to be closed, i.e. endpoints are inclusive.)

I'm using the obvious minimum-maximum check:

int value = (value < 0? 0 : value > 255? 255 : value);

but is there a way to get this faster -- similar to the "modulo" clamp value & 255? And is there a way to do similar things with floating points?

I'm looking for a portable solution, so preferably no CPU/GPU-specific stuff please.


Solution

  • This is a trick I use for clamping an int to a 0 to 255 range:

    /**
     * Clamps the input to a 0 to 255 range.
     * @param v any int value
     * @return {@code v < 0 ? 0 : v > 255 ? 255 : v}
     */
    public static int clampTo8Bit(int v) {
        // if out of range
        if ((v & ~0xFF) != 0) {
            // invert sign bit, shift to fill, then mask (generates 0 or 255)
            v = ((~v) >> 31) & 0xFF;
        }
        return v;
    }
    

    That still has one branch, but a handy thing about it is that you can test whether any of several ints are out of range in one go by ORing them together, which makes things faster in the common case that all of them are in range. For example:

    /** Packs four 8-bit values into a 32-bit value, with clamping. */
    public static int ARGBclamped(int a, int r, int g, int b) {
        if (((a | r | g | b) & ~0xFF) != 0) {
            a = clampTo8Bit(a);
            r = clampTo8Bit(r);
            g = clampTo8Bit(g);
            b = clampTo8Bit(b);
        }
        return (a << 24) + (r << 16) + (g << 8) + (b << 0);
    }