Package com.github.tommyettinger.digital
Class MathTools
java.lang.Object
com.github.tommyettinger.digital.MathTools
Mathematical operations not provided by
Includes code that was originally part of the Uncommon Maths software package as Maths. Also includes code adapted from libGDX as their MathUtils class. There's also
java.lang.Math
.
Includes code that was originally part of the Uncommon Maths software package as Maths. Also includes code adapted from libGDX as their MathUtils class. There's also
cbrt(float)
by Marc B. Reynolds, building on the legendary fast inverse square root,
and a generalized bias/gain function, barronSpline(float, float, float)
, popularized by Jon Barron.-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final float
Thefloat
value that is closer than any other to e, the base of the natural logarithms.static final float
A float that is meant to be used as the smallest reasonable tolerance for methods likeisEqual(float, float, float)
.static final long[]
1275 negative, oddlong
values that are calculated using a generalization of the golden ratio and exponents of those generalizations.static final float
The famous golden ratio,(1.0 + Math.sqrt(5.0)) * 0.5
; this is the "most irrational" of irrational numbers, and has various useful properties.static final float
The inverse of the golden ratio,(1.0 - Math.sqrt(5.0)) * -0.5
orGOLDEN_RATIO - 1.0
; this also has various useful properties. -
Method Summary
Modifier and TypeMethodDescriptionstatic double
barronSpline
(double x, double shape, double turning) A generalization on bias and gain functions that can represent both; this version is branch-less.static float
barronSpline
(float x, float shape, float turning) A generalization on bias and gain functions that can represent both; this version is branch-less.static float
cbrt
(float x) An approximation of the cube-root function for float inputs and outputs.static double
clamp
(double value, double min, double max) If the specified value is not greater than or equal to the specified minimum and less than or equal to the specified maximum, adjust it so that it is.static float
clamp
(float value, float min, float max) If the specified value is not greater than or equal to the specified minimum and less than or equal to the specified maximum, adjust it so that it is.static int
clamp
(int value, int min, int max) If the specified value is not greater than or equal to the specified minimum and less than or equal to the specified maximum, adjust it so that it is.static long
clamp
(long value, long min, long max) If the specified value is not greater than or equal to the specified minimum and less than or equal to the specified maximum, adjust it so that it is.static int
fastCeil
(double t) LikeMath.ceil(double)
, but returns an int.static int
fastCeil
(float t) LikeMath.ceil(double)
, but takes a float and returns an int.static int
fastFloor
(double t) LikeMath.floor(double)
, but returns an int.static int
fastFloor
(float t) LikeMath.floor(double)
, but takes a float and returns an int.static long
greatestCommonDivisor
(long a, long b) Determines the greatest common divisor of a pair of natural numbers using the Euclidean algorithm.static boolean
isEqual
(double a, double b, double tolerance) Equivalent to libGDX's isEqual() method in MathUtils; this compares two doubles for equality and allows the given tolerance during comparison.static boolean
isEqual
(float a, float b) Equivalent to libGDX's isEqual() method in MathUtils; this compares two floats for equality and allows just enough tolerance to ignore a rounding error.static boolean
isEqual
(float a, float b, float tolerance) Equivalent to libGDX's isEqual() method in MathUtils; this compares two floats for equality and allows the given tolerance during comparison.static long
isqrt
(long n) Integer square root (using floor), maintaining correct results even for very largelong
values.static float
lerp
(float fromValue, float toValue, float progress) Linearly interpolates between fromValue to toValue on progress position.static float
lerpAngle
(float fromRadians, float toRadians, float progress) Linearly interpolates between two angles in radians.static float
lerpAngleDeg
(float fromDegrees, float toDegrees, float progress) Linearly interpolates between two angles in degrees.static float
lerpAngleTurns
(float fromTurns, float toTurns, float progress) Linearly interpolates between two angles in turns.static double
log
(double base, double arg) Calculate logarithms for arbitrary bases.static long
longFloor
(double t) LikeMath.floor(double)
, but returns a long.static long
longFloor
(float t) LikeMath.floor(double)
, but takes a float and returns a long.static float
map
(float inRangeStart, float inRangeEnd, float outRangeStart, float outRangeEnd, float value) Linearly map a value from one range to another.static int
modularMultiplicativeInverse
(int a) Given any odd inta
, this finds another odd intb
such thata * b == 1
.static long
modularMultiplicativeInverse
(long a) Given any odd longa
, this finds another odd longb
such thata * b == 1L
.static int
nextPowerOfTwo
(int n) Returns the next higher power of two relative ton
, or n if it is already a power of two.static float
norm
(float rangeStart, float rangeEnd, float value) Linearly normalizes value from a range.static long
raiseToPower
(int value, int power) Calculate the first argument raised to the power of the second.static double
remainder
(double op, double d) Like the modulo operator%
, but the result will always match the sign ofd
instead ofop
.static float
sway
(float value) Very similar toTrigTools.sinTurns(float)
with half frequency, orMath.sin(double)
withMath.PI
frequency, but optimized (and shaped) a little differently.static float
swayCubic
(float value) Very similar toTrigTools.sinTurns(float)
with half frequency, orMath.sin(double)
withMath.PI
frequency, but optimized (and shaped) a little differently.static float
swayTight
(float value) Takes any float and produces a float in the 0f to 1f range, with a graph of input to output that looks much like a sine wave, curving to have a flat slope when given an integer input and a steep slope when the input is halfway between two integers, smoothly curving at any points between those extremes.static double
truncate
(double n) Forces precision loss on the given double so very small fluctuations away from an integer will be erased.static float
truncate
(float n) Forces precision loss on the given float so very small fluctuations away from an integer will be erased.static float
zigzag
(float value) Takes any float and produces a float in the -1f to 1f range, with similar inputs producing close to a consistent rate of up and down through the range.
-
Field Details
-
FLOAT_ROUNDING_ERROR
public static final float FLOAT_ROUNDING_ERRORA float that is meant to be used as the smallest reasonable tolerance for methods likeisEqual(float, float, float)
.- See Also:
-
E
public static final float EThefloat
value that is closer than any other to e, the base of the natural logarithms.- See Also:
-
GOLDEN_RATIO
public static final float GOLDEN_RATIOThe famous golden ratio,(1.0 + Math.sqrt(5.0)) * 0.5
; this is the "most irrational" of irrational numbers, and has various useful properties.- See Also:
-
GOLDEN_RATIO_INVERSE
public static final float GOLDEN_RATIO_INVERSEThe inverse of the golden ratio,(1.0 - Math.sqrt(5.0)) * -0.5
orGOLDEN_RATIO - 1.0
; this also has various useful properties.- See Also:
-
GOLDEN_LONGS
public static final long[] GOLDEN_LONGS1275 negative, oddlong
values that are calculated using a generalization of the golden ratio and exponents of those generalizations. Mostly, these are useful because they are all 64-bit constants that have an irrational-number-like pattern to their bits, which makes them pretty much all useful as increments for large counters (also called Weyl sequences) and also sometimes as multipliers for data that should be somewhat random. The earlier numbers in the array are closer to the bit patterns of irrational numbers. Note that these are not at all uniformly-distributed, and should not be used for tasks where random negative longs must be uniform.
-
-
Method Details
-
raiseToPower
public static long raiseToPower(int value, int power) Calculate the first argument raised to the power of the second. This method only supports non-negative powers.- Parameters:
value
- The number to be raised.power
- The exponent (must be positive).- Returns:
value
raised topower
.
-
log
public static double log(double base, double arg) Calculate logarithms for arbitrary bases.- Parameters:
base
- The base for the logarithm.arg
- The value to calculate the logarithm for.- Returns:
- The log of
arg
in the specifiedbase
.
-
isEqual
public static boolean isEqual(double a, double b, double tolerance) Equivalent to libGDX's isEqual() method in MathUtils; this compares two doubles for equality and allows the given tolerance during comparison. An example is0.3 - 0.2 == 0.1
vs.isEqual(0.3 - 0.2, 0.1, 0.000001)
; the first is incorrectly false, while the second is correctly true.- Parameters:
a
- the first float to compareb
- the second float to comparetolerance
- the maximum difference between a and b permitted for this to return true, inclusive- Returns:
- true if a and b have a difference less than or equal to tolerance, or false otherwise.
-
isEqual
public static boolean isEqual(float a, float b) Equivalent to libGDX's isEqual() method in MathUtils; this compares two floats for equality and allows just enough tolerance to ignore a rounding error. An example is0.3f - 0.2f == 0.1f
vs.isEqual(0.3f - 0.2f, 0.1f)
; the first is incorrectly false, while the second is correctly true.- Parameters:
a
- the first float to compareb
- the second float to compare- Returns:
- true if a and b are equal or extremely close to equal, or false otherwise.
-
isEqual
public static boolean isEqual(float a, float b, float tolerance) Equivalent to libGDX's isEqual() method in MathUtils; this compares two floats for equality and allows the given tolerance during comparison. An example is0.3f - 0.2f == 0.1f
vs.isEqual(0.3f - 0.2f, 0.1f, 0.000001f)
; the first is incorrectly false, while the second is correctly true.- Parameters:
a
- the first float to compareb
- the second float to comparetolerance
- the maximum difference between a and b permitted for this to return true, inclusive- Returns:
- true if a and b have a difference less than or equal to tolerance, or false otherwise.
-
clamp
public static int clamp(int value, int min, int max) If the specified value is not greater than or equal to the specified minimum and less than or equal to the specified maximum, adjust it so that it is.
Note that it can often be just as easy to directly call the same code this calls, while being slightly friendlier to inlining in large method:Math.min(Math.max(value, min), max)
.- Parameters:
value
- The value to check.min
- The minimum permitted value.max
- The maximum permitted value.- Returns:
value
if it is between the specified limits,min
if the value is too low, ormax
if the value is too high.
-
clamp
public static long clamp(long value, long min, long max) If the specified value is not greater than or equal to the specified minimum and less than or equal to the specified maximum, adjust it so that it is.
Note that it can often be just as easy to directly call the same code this calls, while being slightly friendlier to inlining in large method:Math.min(Math.max(value, min), max)
.- Parameters:
value
- The value to check.min
- The minimum permitted value.max
- The maximum permitted value.- Returns:
value
if it is between the specified limits,min
if the value is too low, ormax
if the value is too high.
-
clamp
public static double clamp(double value, double min, double max) If the specified value is not greater than or equal to the specified minimum and less than or equal to the specified maximum, adjust it so that it is.
Note that it can often be just as easy to directly call the same code this calls, while being slightly friendlier to inlining in large method:Math.min(Math.max(value, min), max)
.- Parameters:
value
- The value to check.min
- The minimum permitted value.max
- The maximum permitted value.- Returns:
value
if it is between the specified limits,min
if the value is too low, ormax
if the value is too high.
-
clamp
public static float clamp(float value, float min, float max) If the specified value is not greater than or equal to the specified minimum and less than or equal to the specified maximum, adjust it so that it is.
Note that it can often be just as easy to directly call the same code this calls, while being slightly friendlier to inlining in large method:Math.min(Math.max(value, min), max)
.- Parameters:
value
- The value to check.min
- The minimum permitted value.max
- The maximum permitted value.- Returns:
value
if it is between the specified limits,min
if the value is too low, ormax
if the value is too high.
-
remainder
public static double remainder(double op, double d) Like the modulo operator%
, but the result will always match the sign ofd
instead ofop
.- Parameters:
op
- the dividend; negative values are permitted and wrap instead of producing negative resultsd
- the divisor; if this is negative then the result will be negative, otherwise it will be positive- Returns:
- the remainder of the division of op by d, with a sign matching d
-
greatestCommonDivisor
public static long greatestCommonDivisor(long a, long b) Determines the greatest common divisor of a pair of natural numbers using the Euclidean algorithm. This method only works with natural numbers. If negative integers are passed in, the absolute values will be used. The return value is always positive.- Parameters:
a
- The first value.b
- The second value.- Returns:
- The greatest common divisor.
-
modularMultiplicativeInverse
public static int modularMultiplicativeInverse(int a) Given any odd inta
, this finds another odd intb
such thata * b == 1
.
This is incompatible with GWT, but it should usually only find uses in exploratory code or in tests anyway... It is only incompatible because it tends to rely on multiplication overflow to work. The overload that takes a long and gets the inverse modulo (2 to the 64) is GWT-compatible.- Parameters:
a
- any odd int; note that even numbers do not have inverses modulo 2 to the 32- Returns:
- the multiplicative inverse of
a
modulo 4294967296 (or, 2 to the 32)
-
modularMultiplicativeInverse
public static long modularMultiplicativeInverse(long a) Given any odd longa
, this finds another odd longb
such thata * b == 1L
.- Parameters:
a
- any odd long; note that even numbers do not have inverses modulo 2 to the 64- Returns:
- the multiplicative inverse of
a
modulo 18446744073709551616 (or, 2 to the 64)
-
isqrt
public static long isqrt(long n) Integer square root (using floor), maintaining correct results even for very largelong
values. This version treats negative inputs as unsigned and returns positive square roots for them (these are usually large).
This is based on code recently added to Python, but isn't identical. Notably, this doesn't branch except in the for loop, and it handles negative inputs differently.- Parameters:
n
- along
value that will be treated as if unsigned- Returns:
- the square root of n, rounded down to the next lower
long
if the result isn't already along
-
cbrt
public static float cbrt(float x) An approximation of the cube-root function for float inputs and outputs. This can be about twice as fast asMath.cbrt(double)
. It correctly returns negative results when given negative inputs.
Has very low relative error (less than 1E-9) when inputs are uniformly distributed between -512 and 512, and absolute mean error of less than 1E-6 in the same scenario. Uses a bit-twiddling method similar to one presented in Hacker's Delight and also used in early 3D graphics (see Wikipedia for more, but this code approximates cbrt(x) and not 1/sqrt(x)). This specific code was originally by Marc B. Reynolds, posted in his "Stand-alone-junk" repo .- Parameters:
x
- any finite float to find the cube root of- Returns:
- the cube root of x, approximated
-
barronSpline
public static float barronSpline(float x, float shape, float turning) A generalization on bias and gain functions that can represent both; this version is branch-less. This is based on this micro-paper by Jon Barron, which generalizes the earlier bias and gain rational functions by Schlick. The second and final page of the paper has useful graphs of what the s (shape) and t (turning point) parameters do; shape should be 0 or greater, while turning must be between 0 and 1, inclusive. This effectively combines two different curving functions so they continue into each other when x equals turning. The shape parameter will cause this to imitate "smoothstep-like" splines when greater than 1 (where the values ease into their starting and ending levels), or to be the inverse when less than 1 (where values start like square root does, taking off very quickly, but also end like square does, landing abruptly at the ending level). You should only give x values between 0 and 1, inclusive.- Parameters:
x
- progress through the spline, from 0 to 1, inclusiveshape
- must be greater than or equal to 0; values greater than 1 are "normal interpolations"turning
- a value between 0.0 and 1.0, inclusive, where the shape changes- Returns:
- a float between 0 and 1, inclusive
-
barronSpline
public static double barronSpline(double x, double shape, double turning) A generalization on bias and gain functions that can represent both; this version is branch-less. This is based on this micro-paper by Jon Barron, which generalizes the earlier bias and gain rational functions by Schlick. The second and final page of the paper has useful graphs of what the s (shape) and t (turning point) parameters do; shape should be 0 or greater, while turning must be between 0 and 1, inclusive. This effectively combines two different curving functions so they continue into each other when x equals turning. The shape parameter will cause this to imitate "smoothstep-like" splines when greater than 1 (where the values ease into their starting and ending levels), or to be the inverse when less than 1 (where values start like square root does, taking off very quickly, but also end like square does, landing abruptly at the ending level). You should only give x values between 0 and 1, inclusive.- Parameters:
x
- progress through the spline, from 0 to 1, inclusiveshape
- must be greater than or equal to 0; values greater than 1 are "normal interpolations"turning
- a value between 0.0 and 1.0, inclusive, where the shape changes- Returns:
- a double between 0 and 1, inclusive
-
longFloor
public static long longFloor(double t) LikeMath.floor(double)
, but returns a long. Doesn't consider "weird doubles" like INFINITY and NaN.- Parameters:
t
- the double to find the floor for- Returns:
- the floor of t, as a long
-
longFloor
public static long longFloor(float t) LikeMath.floor(double)
, but takes a float and returns a long. Doesn't consider "weird floats" like INFINITY and NaN.- Parameters:
t
- the double to find the floor for- Returns:
- the floor of t, as a long
-
fastFloor
public static int fastFloor(double t) LikeMath.floor(double)
, but returns an int. Doesn't consider "weird doubles" like INFINITY and NaN.- Parameters:
t
- the float to find the floor for- Returns:
- the floor of t, as an int
-
fastFloor
public static int fastFloor(float t) LikeMath.floor(double)
, but takes a float and returns an int. Doesn't consider "weird floats" like INFINITY and NaN. This method will only properly floor floats from-16384
toFloat.MAX_VALUE - 16384
.
Taken from libGDX MathUtils.- Parameters:
t
- the float to find the floor for- Returns:
- the floor of t, as an int
-
fastCeil
public static int fastCeil(double t) LikeMath.ceil(double)
, but returns an int. Doesn't consider "weird doubles" like INFINITY and NaN.- Parameters:
t
- the float to find the ceiling for- Returns:
- the ceiling of t, as an int
-
fastCeil
public static int fastCeil(float t) LikeMath.ceil(double)
, but takes a float and returns an int. Doesn't consider "weird floats" like INFINITY and NaN.- Parameters:
t
- the float to find the ceiling for- Returns:
- the ceiling of t, as an int
-
nextPowerOfTwo
public static int nextPowerOfTwo(int n) Returns the next higher power of two relative ton
, or n if it is already a power of two. This returns 2 if n is any value less than 2 (including negative numbers, but also 1, which is a power of two).- Parameters:
n
- the lower bound for the result- Returns:
- the next higher power of two that is greater than or equal to n
-
truncate
public static float truncate(float n) Forces precision loss on the given float so very small fluctuations away from an integer will be erased. This is meant primarily for cleaning up floats, so they can be presented without needing scientific notation. It leaves about 3 decimal digits after the point intact, and should make any digits after that simply 0.- Parameters:
n
- any float, but typically a fairly small one (between -8 and 8, as a guideline)- Returns:
n
with its 13 least significant bits effectively removed
-
truncate
public static double truncate(double n) Forces precision loss on the given double so very small fluctuations away from an integer will be erased. This is meant primarily for cleaning up doubles, so they can be presented without needing scientific notation. It leaves about 3 decimal digits after the point intact, and should make any digits after that simply 0.- Parameters:
n
- any double, but typically a fairly small one (between -8 and 8, as a guideline)- Returns:
n
with its 42 least significant bits effectively removed
-
lerp
public static float lerp(float fromValue, float toValue, float progress) Linearly interpolates between fromValue to toValue on progress position.- Parameters:
fromValue
- starting float value; can be any finite floattoValue
- ending float value; can be any finite floatprogress
- how far the interpolation should go, between 0 (equal to fromValue) and 1 (equal to toValue)
-
norm
public static float norm(float rangeStart, float rangeEnd, float value) Linearly normalizes value from a range. Range must not be empty. This is the inverse oflerp(float, float, float)
.- Parameters:
rangeStart
- range start normalized to 0rangeEnd
- range end normalized to 1value
- value to normalize- Returns:
- normalized value; values outside the range are not clamped to 0 and 1
-
map
public static float map(float inRangeStart, float inRangeEnd, float outRangeStart, float outRangeEnd, float value) Linearly map a value from one range to another. Input range must not be empty. This is the same as chainingnorm(float, float, float)
from input range andlerp(float, float, float)
to output range.- Parameters:
inRangeStart
- input range startinRangeEnd
- input range endoutRangeStart
- output range startoutRangeEnd
- output range endvalue
- value to map- Returns:
- mapped value; values outside the input range are not clamped to output range
-
lerpAngle
public static float lerpAngle(float fromRadians, float toRadians, float progress) Linearly interpolates between two angles in radians. Takes into account that angles wrap atPI2
and always takes the direction with the smallest delta angle.- Parameters:
fromRadians
- start angle in radianstoRadians
- target angle in radiansprogress
- interpolation value in the range [0, 1]- Returns:
- the interpolated angle in the range [0, PI2)
-
lerpAngleDeg
public static float lerpAngleDeg(float fromDegrees, float toDegrees, float progress) Linearly interpolates between two angles in degrees. Takes into account that angles wrap at 360 degrees and always takes the direction with the smallest delta angle.- Parameters:
fromDegrees
- start angle in degreestoDegrees
- target angle in degreesprogress
- interpolation value in the range [0, 1]- Returns:
- the interpolated angle in the range [0, 360)
-
lerpAngleTurns
public static float lerpAngleTurns(float fromTurns, float toTurns, float progress) Linearly interpolates between two angles in turns. Takes into account that angles wrap at 1.0 and always takes the direction with the smallest delta angle. This version, unlike the versions for radians and degrees, avoids any modulus operation (instead callingfastFloor(float)
twice).- Parameters:
fromTurns
- start angle in turnstoTurns
- target angle in turnsprogress
- interpolation value in the range [0, 1]- Returns:
- the interpolated angle in the range [0, 1)
-
zigzag
public static float zigzag(float value) Takes any float and produces a float in the -1f to 1f range, with similar inputs producing close to a consistent rate of up and down through the range. This is meant for noise, where it may be useful to limit the amount of change between nearby points' noise values and prevent sudden "jumps" in noise value. An input of any even number should produce something very close to -1f, any odd number should produce something very close to 1f, and any number halfway between two incremental integers (like 8.5f or -10.5f) should produce 0f or a very small fraction. This method is closely related tosway(float)
, which will smoothly curve its output to produce more values that are close to -1 or 1.- Parameters:
value
- any float- Returns:
- a float from -1f (inclusive) to 1f (inclusive)
-
sway
public static float sway(float value) Very similar toTrigTools.sinTurns(float)
with half frequency, orMath.sin(double)
withMath.PI
frequency, but optimized (and shaped) a little differently. This looks like a squished sine wave when graphed, and is essentially just interpolating between each pair of odd and even inputs using what FastNoise callsQUINTIC
interpolation. This interpolation is slightly flatter at peaks and valleys than a sine wave is.
An input of any even number should produce something very close to -1f, any odd number should produce something very close to 1f, and any number halfway between two incremental integers (like 8.5f or -10.5f) should produce 0f or a very small fraction. In the (unlikely) event that this is given a float that is too large to represent many or any non-integer values, this will simply return -1f or 1f.
This version of a sway method uses quintic interpolation; it uses up to the fifth power of value.- Parameters:
value
- any float other than NaN or infinite values; extremely large values can't work properly- Returns:
- a float from -1f (inclusive) to 1f (inclusive)
-
swayCubic
public static float swayCubic(float value) Very similar toTrigTools.sinTurns(float)
with half frequency, orMath.sin(double)
withMath.PI
frequency, but optimized (and shaped) a little differently. This looks like a squished sine wave when graphed, and is essentially just interpolating between each pair of odd and even inputs using what is sometimes calledHERMITE
interpolation. This interpolation is rounder at peaks and valleys than a sine wave is; it is also calledsmoothstep
in GLSL, and is called cubic here because it gets the third power of a value.
An input of any even number should produce something very close to -1f, any odd number should produce something very close to 1f, and any number halfway between two incremental integers (like 8.5f or -10.5f) should produce 0f or a very small fraction. In the (unlikely) event that this is given a float that is too large to represent many or any non-integer values, this will simply return -1f or 1f.- Parameters:
value
- any float other than NaN or infinite values; extremely large values can't work properly- Returns:
- a float from -1f (inclusive) to 1f (inclusive)
-
swayTight
public static float swayTight(float value) Takes any float and produces a float in the 0f to 1f range, with a graph of input to output that looks much like a sine wave, curving to have a flat slope when given an integer input and a steep slope when the input is halfway between two integers, smoothly curving at any points between those extremes. This is meant for noise, where it may be useful to limit the amount of change between nearby points' noise values and prevent both sudden "jumps" in noise value and "cracks" where a line takes a sudden jagged movement at an angle.
An input of any even number should produce something very close to 0f, any odd number should produce something very close to 1f, and any number halfway between two incremental integers (like 8.5f or -10.5f) should produce 0.5f. In the (unlikely) event that this is given a float that is too large to represent many or any non-integer values, this will simply return 0f or 1f. This version is called "Tight" because its range is tighter thansway(float)
.
This version of a sway method uses quintic interpolation; it uses up to the fifth power of value.- Parameters:
value
- any float other than NaN or infinite values; extremely large values can't work properly- Returns:
- a float from 0f (inclusive) to 1f (inclusive)
-