6

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.

Mark Jeronimus
  • 9,278
  • 3
  • 37
  • 50
hsnsd
  • 1,728
  • 12
  • 30
  • 4
    Open `(-0.5; 0.5)` or closed `<-0.5; 0.5>` interval? `[-0.5; 0.5]` is no interval but a set of nubers, mathematically. – Nikolas Charalambidis May 10 '19 at 08:33
  • And what precision? `0.1` or `0.01` or `0.001`...? – Nikolas Charalambidis May 10 '19 at 08:35
  • No specific precision, if thats possible. – hsnsd May 10 '19 at 08:36
  • I read `[...]` as a close interval to be honest. However, this is possibly a duplicate: https://stackoverflow.com/questions/5887709/getting-random-numbers-in-java – doctorlove May 10 '19 at 08:37
  • 2
    The probability of getting 0 or 1 is 2/(1.84467440737096e+19). Are you sure the requirement makes sense? – gpunto May 10 '19 at 08:42
  • @gpunto it seems that odds of getting 1 is of integer value equal to **0**. – Antoniossss May 10 '19 at 08:45
  • 4
    Why do you care about the range being inclusive when the precision is so high that the probability of getting 0.5 is basically 0? – marstran May 10 '19 at 08:46
  • 1
    Is diviging by 2 probablility of getting lower bound and uppoer boud is acceptable for you?? :) If so, return lower bound on upper bound everytime lower bound is returned by random generator. Probablility wont be uniform for whole range, but whole range will be included in process. – Antoniossss May 10 '19 at 08:54

6 Answers6

8

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.

OH GOD SPIDERS
  • 3,091
  • 2
  • 13
  • 16
3

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);
Peter Walser
  • 15,208
  • 4
  • 51
  • 78
  • 1
    The deltas between the double values are **not constant**, due to the way double numbers are represented internally. In fact, `0.5 + Double.MIN_VALUE > 0.5` is `false`. To find the next larger number, one has to find out the epsilon in the exact vincinity of the number. – Peter Walser May 10 '19 at 10:01
  • Using knowledge of the IEEE floating-point representation that the JVM uses, I think you can get the actual max+epsilon using `max = Double.longBitsToDouble(Double.doubleToLongBits(max) + 1L);` – DodgyCodeException May 10 '19 at 12:48
  • @DodgyCodeException: nice, results in the same max + epsilon, added it to answer, thanks! – Peter Walser May 10 '19 at 13:57
3

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.

Mark Jeronimus
  • 9,278
  • 3
  • 37
  • 50
1

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));
PopHip
  • 700
  • 6
  • 23
1

@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;
DodgyCodeException
  • 5,963
  • 3
  • 21
  • 42
0

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;

Nav_cfc
  • 154
  • 1
  • 4
  • 15
  • 4
    Actually, it is **[0,1)**: *The general contract of nextDouble is that one double value, chosen (approximately) uniformly from the range 0.0d (inclusive) to 1.0d (exclusive), is pseudorandomly generated and returned.* – Antoniossss May 10 '19 at 08:48
  • 3
    This code is basically the same as the OP's code. `Math.random()` actually calls `nextDouble()`. – DodgyCodeException May 10 '19 at 08:50