For my own games, I’ve created some math utilities which might be worth incorporating into FastMath. Here’s how it might look:
--- HEAD
+++ Modified In Working Tree
@@ -957,4 +957,199 @@
| ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00)
| ((f >> 13) & 0x03ff));
}
+
+ /**
+ * Compute the circle function sqrt(1 - x^2) for a single-precision value.
+ * Double precision arithmetic is used to reduce the risk of overflow.
+ *
+ * @param abscissa input (≤1, ≥-1)
+ * @return positive ordinate of the unit circle at the abscissa (≤1,
+ * ≥0)
+ */
+ public static float circle(float abscissa) {
+ if (!(abscissa >= -1f && abscissa <= 1f)) {
+ throw new IllegalArgumentException(
+ "abscissa should be between -1 and 1, inclusive");
+ }
+
+ double x = (double) abscissa;
+ float y = (float) Math.sqrt(1.0 - x * x);
+
+ assert y >= 0f : y;
+ assert y <= 1f : y;
+ return y;
+ }
+
+ /**
+ * Clamp the magnitude of a single-precision value.
+ *
+ * @param fValue input value to be clamped
+ * @param maxMagnitude limit of the clamp (≥0)
+ * @return value between -maxMagnitude and +maxMagnitude inclusive which is
+ * closest to fValue
+ * @see #clamp(float,float,float)
+ */
+ public static float clamp(float fValue, float maxMagnitude) {
+ if (!(maxMagnitude >= 0f)) {
+ throw new IllegalArgumentException("limit should not be negative");
+ }
+
+ return clamp(fValue, -maxMagnitude, maxMagnitude);
+ }
+
+ /**
+ * Cube a single-precision value.
+ *
+ * @param fValue input value to be cubed
+ * @return fValue raised to the third power
+ * @see #cubeRoot(float)
+ */
+ public static float cube(float fValue) {
+ return fValue * fValue * fValue;
+ }
+
+ /**
+ * Extract the cube root of a single-precision value. Unlike
+ * #pow(float,float), this method works on negative values.
+ *
+ * @param fValue input cube to be extracted (may be negative)
+ * @return cube root of fValue
+ * @see #cube(float)
+ * @see #pow(float,float)
+ * @see Math#cbrt(double)
+ */
+ public static float cubeRoot(float fValue) {
+ float magnitude = abs(fValue);
+ float exponent = ONE_THIRD;
+ float rootMagnitude = pow(magnitude, exponent);
+ float result = copysign(rootMagnitude, fValue);
+
+ return result;
+ }
+
+ /**
+ * Fade polynomial for Perlin noise. Double precision arithmetic is used to
+ * reduce rounding error.
+ *
+ * @param t input value (≤1, ≥0)
+ * @return 6*t^5 - 15*t^4 + 10*t^3 (≤1, ≥0)
+ */
+ public static float fade(float t) {
+ if (!(t >= 0f && t <= 1f)) {
+ throw new IllegalArgumentException(
+ "input value should be between 0 and 1, inclusive");
+ }
+
+ double tt = (double) t;
+ double ff = tt * tt * tt * (10.0 + tt * (-15.0 + 6.0 * tt));
+ float result = (float) ff;
+
+ assert result >= 0f : result;
+ assert result <= 1f : result;
+ return result;
+ }
+
+ /**
+ * Compute the hypotenuse of a right triangle using the Pythagorean Theorem.
+ * This method accepts negative arguments.
+ *
+ * @param legA length of the 1st leg (may be negative)
+ * @param legB length of the 2nd leg (may be negative)
+ * @return length of the hypotenuse (≥0)
+ */
+ public static float hypotenuse(float legA, float legB) {
+ double x = (double) legA;
+ double y = (double) legB;
+ double sumSquares = x * x + y * y;
+ float result = (float) Math.sqrt(sumSquares);
+
+ assert result >= 0f : result;
+ return result;
+ }
+
+ /**
+ * Test whether an integer value is odd.
+ *
+ * @param iValue input value to be tested
+ * @return true if x is odd, false if it's even
+ */
+ public static boolean isOdd(int iValue) {
+ boolean result = (iValue % 2) != 0;
+ return result;
+ }
+
+ /**
+ * Find the max of three single-precision values.
+ *
+ * @param a 1st input value
+ * @param b 2nd input value
+ * @param c 3rd input value
+ * @return greatest of the three values
+ */
+ public static float max(float a, float b, float c) {
+ if (a >= b && a >= c) {
+ return a;
+ } else if (b >= c) {
+ return b;
+ } else {
+ return c;
+ }
+ }
+
+ /**
+ * Compute the least non-negative value congruent with a single-precision
+ * value with respect to the specified modulus.
+ *
+ * @param fValue input value
+ * @param modulus (>0)
+ * @return fValue MOD modulus (<modulus, ≥0)
+ */
+ public static float modulo(float fValue, float modulus) {
+ if (!(modulus > 0f)) {
+ throw new IllegalArgumentException("modulus should be positive");
+ }
+
+ float result = (fValue % modulus + modulus) % modulus;
+
+ assert result >= 0f : result;
+ assert result < modulus : result;
+ return result;
+ }
+
+ /**
+ * Compute the least non-negative value congruent with an integer value with
+ * respect to the specified modulus.
+ *
+ * @param iValue input value
+ * @param modulus (>0)
+ * @return iValue MOD modulus (<modulus, ≥0)
+ */
+ public static int modulo(int iValue, int modulus) {
+ if (modulus <= 0) {
+ throw new IllegalArgumentException("modulus should be positive");
+ }
+
+ int result = (iValue % modulus + modulus) % modulus;
+
+ assert result >= 0f : result;
+ assert result < modulus : result;
+ return result;
+ }
+
+ /**
+ * Standardize a rotation angle to the range [-Pi, Pi).
+ *
+ * @param angle input (in radians)
+ * @return standardized angle (in radians, <Pi, ≥-Pi)
+ */
+ public static float standardizeAngle(float angle) {
+ float result = modulo(angle, TWO_PI);
+ if (result >= PI) {
+ result -= TWO_PI;
+ }
+
+ assert result >= -PI : result;
+ assert result < PI : result;
+ return result;
+ }
}
Do any of these seem worth adding?