//////////////////////////////////////// // Programming project code // UniBw M, 2022, 2023, 2024 // www.unibw.de/inf2 // (c) Mark Minas (mark.minas@unibw.de) //////////////////////////////////////// package pp.util; /** * Provides mathematical functions using float precision. */ public class FloatMath { private FloatMath() { /* don't instantiate */ } public static final double DBL_EPSILON = 2.220446049250313E-16d; /** * A "close to zero" float epsilon value for use */ public static final float FLT_EPSILON = 1.1920928955078125E-7f; /** * A "close to zero" float epsilon value for use */ public static final float ZERO_TOLERANCE = 0.0001f; /** * The value 1/3, as a float. */ public static final float ONE_THIRD = 1f / 3f; /** * The value PI as a float. (180 degrees) */ public static final float PI = (float) Math.PI; /** * The value 2PI as a float. (360 degrees) */ public static final float TWO_PI = 2.0f * PI; /** * The value PI/2 as a float. (90 degrees) */ public static final float HALF_PI = 0.5f * PI; /** * The value PI/4 as a float. (45 degrees) */ public static final float QUARTER_PI = 0.25f * PI; /** * The value 1/PI as a float. */ public static final float INV_PI = 1.0f / PI; /** * The value 1/(2PI) as a float. */ public static final float INV_TWO_PI = 1.0f / TWO_PI; /** * A value to multiply a degree value by, to convert it to radians. */ public static final float DEG_TO_RAD = PI / 180.0f; /** * A value to multiply a radian value by, to convert it to degrees. */ public static final float RAD_TO_DEG = 180.0f / PI; /** * Linear interpolation from startValue to endValue by the given percent. * Basically: ((1 - percent) * startValue) + (percent * endValue) * * @param scale scale value to use. if 1, use endValue, if 0, use startValue. * @param startValue Beginning value. 0% of f * @param endValue ending value. 100% of f * @return The interpolated value between startValue and endValue. */ public static float interpolateLinear(float scale, float startValue, float endValue) { if (startValue == endValue) { return startValue; } if (scale <= 0f) { return startValue; } if (scale >= 1f) { return endValue; } return ((1f - scale) * startValue) + (scale * endValue); } /** * Linear extrapolation from startValue to endValue by the given scale. * if scale is between 0 and 1 this method returns the same result as interpolateLinear * if the scale is over 1 the value is linearly extrapolated. * Note that the end value is the value for a scale of 1. * * @param scale the scale for extrapolation * @param startValue the starting value (scale = 0) * @param endValue the end value (scale = 1) * @return an extrapolation for the given parameters */ public static float extrapolateLinear(float scale, float startValue, float endValue) { return ((1f - scale) * startValue) + (scale * endValue); } /** * Returns the arc cosine of a value.
* Special cases: * * * @param fValue The value to arc cosine. * @return The angle, in radians. * @see Math#acos(double) */ public static float acos(float fValue) { if (-1.0f < fValue) { if (fValue < 1.0f) { return (float) Math.acos(fValue); } return 0.0f; } return PI; } /** * Returns the arc sine of a value.
* Special cases: * * * @param fValue The value to arc sine. * @return the angle in radians. * @see Math#asin(double) */ public static float asin(float fValue) { if (-1.0f < fValue) { if (fValue < 1.0f) { return (float) Math.asin(fValue); } return HALF_PI; } return -HALF_PI; } /** * Returns the arc tangent of an angle given in radians.
* * @param fValue The angle, in radians. * @return fValue's atan * @see Math#atan(double) */ public static float atan(float fValue) { return (float) Math.atan(fValue); } /** * A direct call to Math.atan2. * * @param fY ordinate * @param fX abscissa * @return Math.atan2(fY, fX) * @see Math#atan2(double, double) */ public static float atan2(float fY, float fX) { return (float) Math.atan2(fY, fX); } public static float sinh(float x) { return (float) Math.sinh(x); } public static float cosh(float x) { return (float) Math.cosh(x); } public static float tanh(float x) { return (float) Math.tanh(x); } public static float coth(float x) { return (float) (1d / Math.tanh(x)); } public static float arsinh(float x) { return log(x + sqrt(x * x + 1f)); } public static float arcosh(float x) { return log(x + sqrt(x * x - 1f)); } public static float artanh(float x) { return 0.5f * log((1f + x) / (1f - x)); } public static float arcoth(float x) { return 0.5f * log((x + 1f) / (x - 1f)); } /** * Rounds a fValue up. A call to Math.ceil * * @param fValue The value. * @return The fValue rounded up * @see Math#ceil(double) */ public static float ceil(float fValue) { return (float) Math.ceil(fValue); } /** * Returns cosine of an angle. Direct call to Math * * @param v The angle to cosine. * @return the cosine of the angle. * @see Math#cos(double) */ public static float cos(float v) { return (float) Math.cos(v); } /** * Returns the sine of an angle. Direct call to Math * * @param v The angle to sine. * @return the sine of the angle. * @see Math#sin(double) */ public static float sin(float v) { return (float) Math.sin(v); } /** * Returns E^fValue * * @param fValue Value to raise to a power. * @return The value E^fValue * @see Math#exp(double) */ public static float exp(float fValue) { return (float) Math.exp(fValue); } public static float expm1(float fValue) { return (float) Math.expm1(fValue); } /** * Returns Absolute value of a float. * * @param fValue The value to abs. * @return The abs of the value. * @see Math#abs(float) */ public static float abs(float fValue) { if (fValue < 0) { return -fValue; } return fValue; } /** * Returns a number rounded down. * * @param fValue The value to round * @return The given number rounded down * @see Math#floor(double) */ public static float floor(float fValue) { return (float) Math.floor(fValue); } /** * Returns 1/sqrt(fValue) * * @param fValue The value to process. * @return 1/sqrt(fValue) * @see Math#sqrt(double) */ public static float invSqrt(float fValue) { return (float) (1.0f / Math.sqrt(fValue)); } /** * Quickly estimate 1/sqrt(fValue). * * @param x the input value (≥0) * @return an approximate value for 1/sqrt(x) */ public static float fastInvSqrt(float x) { float halfX = 0.5f * x; int i = Float.floatToIntBits(x); // get bits for floating value i = 0x5f375a86 - (i >> 1); // gives initial guess y0 x = Float.intBitsToFloat(i); // convert bits back to float x = x * (1.5f - halfX * x * x); // Newton step, repeating increases accuracy return x; } /** * Returns the log base E of a value. * * @param fValue The value to log. * @return The log of fValue base E * @see Math#log(double) */ public static float log(float fValue) { return (float) Math.log(fValue); } /** * Returns a number raised to an exponent power. fBase^fExponent * * @param fBase The base value (IE 2) * @param fExponent The exponent value (IE 3) * @return base raised to exponent (IE 8) * @see Math#pow(double, double) */ public static float pow(float fBase, float fExponent) { return (float) Math.pow(fBase, fExponent); } /** * Returns the value squared. fValue ^ 2 * * @param fValue The value to square. * @return The square of the given value. */ public static float sqr(float fValue) { return fValue * fValue; } /** * Returns the square root of a given value. * * @param fValue The value to sqrt. * @return The square root of the given value. * @see Math#sqrt(double) */ public static float sqrt(float fValue) { return (float) Math.sqrt(fValue); } /** * Returns the tangent of the specified angle. * * @param fValue The value to tangent, in radians. * @return The tangent of fValue. * @see Math#tan(double) */ public static float tan(float fValue) { return (float) Math.tan(fValue); } /** * Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise * * @param iValue The integer to examine. * @return The integer's sign. */ public static int sign(int iValue) { return Integer.compare(iValue, 0); } /** * Returns 1 if the number is positive, -1 if the number is negative, and 0 otherwise * * @param fValue The float to examine. * @return The float's sign. */ public static float sign(float fValue) { return Math.signum(fValue); } /** * Take a float input and clamp it between min and max. * * @param input the value to be clamped * @param min the minimum output value * @param max the maximum output value * @return clamped input */ public static float clamp(float input, float min, float max) { return Math.max(min, Math.min(input, max)); } /** * Clamps the given float to be between 0 and 1. * * @param input the value to be clamped * @return input clamped between 0 and 1. */ public static float saturate(float input) { return clamp(input, 0f, 1f); } /** * Determine if two floats are approximately equal. * This takes into account the magnitude of the floats, since * large numbers will have larger differences be close to each other. *

* Should return true for a=100000, b=100001, but false for a=10000, b=10001. * * @param a The first float to compare * @param b The second float to compare * @return True if a and b are approximately equal, false otherwise. */ public static boolean approximateEquals(float a, float b) { if (a == b) { return true; } else { return (abs(a - b) / Math.max(abs(a), abs(b))) <= 0.00001f; } } /** * Normalizes the specified angle to lie in the interval (-pi,pi] and returns the normalized value in radians. * * @param angle the specified angle in radians */ public static float normalizeAngle(float angle) { final float res = angle % TWO_PI; if (res <= -FloatMath.PI) return res + TWO_PI; else if (res > FloatMath.PI) return res - TWO_PI; return res; } }