JDK has a random method in ThreadLocalRandom
:
nextDouble(startInclusive, endExclusive)
But no float
version. Here I want to float version, this is my implementation:
public static float nextFloat(float startInclusive, float endExclusive) {
return (float) nextDouble(startInclusive, endExclusive);
}
I know float number has precision issues when cast double to float. So, is it possible the result float value < startInclusive or >= endExclusive? And how to solve it?
Indeed your current implementation might output a float that is equal to endExclusive
. Suppose next double
returns d
, which is less than endExclusive
, but the closest value to d
representable by a float
could still be endExclusive
.
In modern versions of the JDK, there exists nextFloat
that takes a lower bound and upper bound. If you are on an older version, you can just take a look at how nextFloat
in newer versions are implemented, and do the same thing yourself.
Using the code from RandomSupport.java in the main branch of OpenJDK, you can write such a helper:
public static float nextFloat(Random random, float origin, float bound) {
if (!(Float.NEGATIVE_INFINITY < origin && origin < bound &&
bound < Float.POSITIVE_INFINITY)) {
throw new IllegalArgumentException("Invalid range");
}
float r = random.nextFloat();
if (bound - origin < Float.POSITIVE_INFINITY) {
r = r * (bound - origin) + origin;
} else {
float halfOrigin = 0.5f * origin;
r = (r * (0.5f * bound - halfOrigin) + halfOrigin) * 2.0f;
}
if (r >= bound)
r = Math.nextDown(bound);
return r;
}
This uses the parameterless nextFloat
, which existed for a long time. It returns a random float between 0 and 1 inclusive, and the code simply linearly maps that 0~1 range to the range we want. It is also possible for this process to generate a number greater than or equal to the upper bound, in which case the JDK seems to just use the greatest float that is smaller than bound
(Math.nextDown
).