Last active
July 6, 2022 06:49
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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