Skip to content

Instantly share code, notes, and snippets.

@Sothatsit
Last active July 6, 2022 06:49
Show Gist options
  • Save Sothatsit/2ca2d0fd7c8c74192eb6ca9c4d1dae1e to your computer and use it in GitHub Desktop.
Save Sothatsit/2ca2d0fd7c8c74192eb6ca9c4d1dae1e to your computer and use it in GitHub Desktop.
Clamp Java floats and doubles, and correctly consider -0.0 and +0.0
/**
* An over-the-top implementation of clamp that correctly
* considers -0.0 and +0.0 bounds for floating point numbers.
*
* @author Padraig Lamont, 2021
*/
public class Clamp {
// Use raw bit-wise conversions on guaranteed non-NaN values.
private static final long negativeZeroFloatBits = Float.floatToRawIntBits(-0.0f);
private static final long zeroFloatBits = Float.floatToRawIntBits(0.0f);
private static final long negativeZeroDoubleBits = Double.doubleToRawLongBits(-0.0d);
private static final long zeroDoubleBits = Double.doubleToRawLongBits(0.0d);
/**
* Returns the closest value to {@param value} between {@param bound1} and {@param bound2}.
* If {@param bound1} or {@param bound2} is NaN, returns {@param value} unchanged.
*/
public final static float clamp(float value, float bound1, float bound2) {
if (bound2 < bound1) {
float temp = bound1;
bound1 = bound2;
bound2 = temp;
} else if (bound1 != bound1 || bound2 != bound2) { // NaN
return value;
}
if (value < bound1)
return bound1;
if (value > bound2)
return bound2;
if (value != 0 || (bound1 != 0 && bound2 != 0))
return value;
if (bound1 == bound2) // Both zero, but signs could differ.
return Float.floatToRawIntBits(bound1) == Float.floatToRawIntBits(bound2) ? bound1 : value;
if (Float.floatToRawIntBits(bound1) == negativeZeroFloatBits)
return value;
if (Float.floatToRawIntBits(bound2) == zeroFloatBits)
return value;
return bound1 == 0 ? bound1 : bound2;
}
/**
* Returns the closest value to {@param value} between {@param bound1} and {@param bound2}.
* If {@param bound1} or {@param bound2} is NaN, returns {@param value} unchanged.
*/
public final static double clamp(double value, double bound1, double bound2) {
if (bound2 < bound1) {
double temp = bound1;
bound1 = bound2;
bound2 = temp;
} else if (bound1 != bound1 || bound2 != bound2) { // NaN
return value;
}
if (value < bound1)
return bound1;
if (value > bound2)
return bound2;
if (value != 0 || (bound1 != 0 && bound2 != 0))
return value;
if (bound1 == bound2) // Both zero, but signs could differ.
return Double.doubleToRawLongBits(bound1) == Double.doubleToRawLongBits(bound2) ? bound1 : value;
if (Double.doubleToRawLongBits(bound1) == negativeZeroDoubleBits)
return value;
if (Double.doubleToRawLongBits(bound2) == zeroDoubleBits)
return value;
return bound1 == 0 ? bound1 : bound2;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment