hsnsd
hsnsd

Reputation: 1823

Generate random float, both bounds inclusive

I need to generate random real numbers in the range [-0.5, 0.5], both bounds inclusive.

I found various ways to generate similar ranges, like

-0.5 + Math.random()

But the upper bound is always exclusive, I need it inclusive as well. 0.5 must be inside the range.

Upvotes: 6

Views: 2769

Answers (6)

Peter Walser
Peter Walser

Reputation: 15706

You can adjust the upper bound by the minimal value (epsilon) larger than the maxium value you expect. To find the epsilon, start with any positive value and make it as small as it can get:

double min = -0.5;
double max = 0.5;

double epsilon = 1;
while (max + epsilon / 2 > max) {
    epsilon /= 2;
}

Random random = ThreadLocalRandom.current();
DoubleStream randomDoubles = random.doubles(min, max + epsilon);

Edit: alternative suggested by @DodgyCodeException (results in same epsilon as above):

double min = -0.5;
double max = 0.5;

double maxPlusEpsilon = Double.longBitsToDouble(Double.doubleToLongBits(max) + 1L)

Random random = ThreadLocalRandom.current();
DoubleStream randomDoubles = random.doubles(min, maxPlusEpsilon);

Upvotes: 2

Mark Jeronimus
Mark Jeronimus

Reputation: 9541

I haven't seen any answer that uses bit-fiddling inside the IEEE-754 Double representation, so here's one.

Based on the observation that a rollover to a next binary exponent is the same as adding 1 to the binary representation (actually this is by design):

Double.longBitsToDouble(0x3ff0000000000000L) // 1.0
Double.longBitsToDouble(0x3ffFFFFFFFFFFFFFL) // 1.9999999999999998
Double.longBitsToDouble(0x4000000000000000L) // 2.0

I came up with this:

long   l = ThreadLocalRandom.current().nextLong(0x0010000000000001L);
double r = Double.longBitsToDouble(l + 0x3ff0000000000000L) - 1.5;

This technique works only with ranges that span a binary number (1, 2, 4, 8, 0.5, 0.25, etc) but for those ranges this approach is possibly the most efficient and accurate. This example is tuned for a span of 1. For ranges that do not span a binary range, you can still use this technique to get a different span. Apply the technique to get a number in the range [0, 1] and scale the result to the desired span. This has negligible accuracy loss, and the resulting accuracy is actually identical to that of Random.nextDouble(double, double).

For other spans, execute this code to find the offset:

double span = 0.125;

if (!(span > 0.0) || (Double.doubleToLongBits(span) & 0x000FFFFFFFFFFFFFL) != 0)
    throw new IllegalArgumentException("'span' is not a binary number: " + span);
if (span * 2 >= Double.MAX_VALUE)
    throw new IllegalArgumentException("'span' is too large: " + span);

System.out.println("Offset: 0x" + Long.toHexString(Double.doubleToLongBits(span)));

When you plug this offset into the second line of the actual code, you get a value in the range [span, 2*span]. Subtract the span to get a value starting at 0.

Upvotes: 2

user6073886
user6073886

Reputation:

One way to achieve this would be to create random int from -500 to 500 and then divide it by 1000.

int max = 500;
int min = -500;
int randomInt = rand.nextInt((max - min) + 1) + min;
float randomNum = randomInt / 1000.00f;
System.out.println(randomNum);

You can change the precision by adding and removing zeros from the integer boundaries and the divisor. (eG: create integers from -5 to +5 and divide by 10 for less precision)

A disadvantage of that solution is that it does not use the maximum precision provided by float/double data types.

Upvotes: 8

DodgyCodeException
DodgyCodeException

Reputation: 6123

@OH GOD SPIDERS' answer gave me an idea to develop it into an answer that gives greater precision. nextLong() gives a value between MIN_VALUE and MAX_VALUE with more than adequate precision when cast to double.

double randomNum = (rand.nextLong() / 2.0) / Long.MAX_VALUE;

Proof that bounds are inclusive:

assert (Long.MIN_VALUE/2.0)/Long.MAX_VALUE == -0.5;
assert (Long.MAX_VALUE/2.0)/Long.MAX_VALUE == 0.5;

Upvotes: 0

PopHip
PopHip

Reputation: 768

Given HOW GOD SPIDERS answer, here is a ready to use function :

public static double randomFloat(double minInclusive, double maxInclusive, double precision) {
    int max = (int)(maxInclusive/precision);
    int min = (int)(minInclusive/precision);
    Random rand = new Random();
    int randomInt = rand.nextInt((max - min) + 1) + min;
    double randomNum = randomInt * precision;
    return randomNum;
}

then

System.out.print(randomFloat(-0.5, 0.5, 0.01));

Upvotes: 0

Nav_cfc
Nav_cfc

Reputation: 164

Random.nextDouble gives a value in the range of [0, 1]. So to map that to a range of [-0.5, 0.5] you just need to subtract by 0.5.

You can use this code to get the desired output double value = r.nextDouble() - 0.5;

Upvotes: 0

Related Questions