Randomly generating floats spanning the whole spectrum of valid floats - java

As part of random testing, I'm looking to generate random floats spanning the whole spectrum of valid floats (but excluding +/- infinity and NaN). I want very, very large numbers and minuscule numbers, both positive and negative.
Here's what I'm doing:
Random r = new Random();
float random_value = (r.nextFloat() - 0.5f) * Float.MAX_VALUE;
This seems like it should in theory work, but it only generates very large values. (ie. in the range of 1037 – 1038, positive and negative)
Any suggestions?

Your code should create uniformly distributed float values, that is, a uniform distribution in a purely mathematical sense. However, most of these values will be in the 1037-1038 range, simply because there are more mathematical values in that range than in the lower, everyday value range. In the real domain, there are 10 times as many values in the range 1037-1038 as 1036-1037, 10 times as many values in the range 1036-1037 as 1035-1036, and so on, so it's no surprise that just about all of them are of extremely large magnitude.
What I think you want is a completely random float value, with a range only in the valid float domain. Because a float is specified by an exponent and a mantissa, the limiting factor as to how many values are available in a given range is limited by the precision, not how many mathematical values are available. Specifically, there are precisely the same number of float values between a and b as there are between 2 * a and 2 * b, provided all values are within the float domain.
There is a one-to-one correspondence between all possible int values and all possible float values (including infinities and NaN), because both primitive types are represented by 32 bits.
Choose a random int value, across all possible int values, positive, 0, and negative, and convert the bit representation of the int to the corresponding bit representation of the float you want. You'll have to choose a random long and downcast to int. If you randomly get an infinity or an NaN, try again.
Random rnd = new Random();
float randomFloat = 0.0f;
do {
// downcast will cover negative int value range.
int randomInt = (int) rnd.nextLong(1L << 32);
float randomFloat = Float.intBitsToFloat(randomInt);
// Discard infinities and NaNs.
} while (randomFloat == Float.NEGATIVE_INFINITY ||
randomFloat == Float.POSITIVE_INFINITY ||
Float.isNaN(randomFloat));

You can get an uniform distribution by generating your floats using bits instead of arithmetic.
This could be an option:
public static float randomFloat() {
// Generate a random integer. These are uniform over the 32 bit words
Random r = new Random();
int intBits = r.nextInt();
// Make a float from the integer's bits
float f = Float.intBitsToFloat(intBits);
// Handle non-numeric cases
if (Float.isInfinite(f) || Float.isNaN(f)) {
// Only happens ~ 1/250 times
return randomFloat();
} else {
return f;
}
}

You are probably hitting precision issues. Try doing the multiplication at double precision and then casting down to a float afterwards.
i.e.
float random_value = (float)((r.nextDouble() - 0.5) * Float.MAX_VALUE);

Firstly you want to generate a random float between -1 and 1. To do this:
Random r = new Random();
float random_value = r.nextFloat() * 2 - 1
and then scale that:
Random r = new Random();
float random_value = (r.nextFloat() * 2 - 1) * Float.MAX_VALUE

Related

How do you generate a random decimal with decimals above 0 in Java?

I need to generate a random decimal from 0.85 to 1. I saw on other questions a lot of different methods, but all of them count on the beginning number to be 0. How do I do this?
Well, Math.random() will generate a number from 0 to 1, so Math.random()/100 will generate a number from 0 to 0.01, so Math.random()/100*15 will generate a number from 0 to 0.15, so Math.random()/100*15 + 0.85 will generate a number from 0.85 to 1
Another way to think of it:
double min = 0.85;
double max = 1.0;
double value = Math.random() * (max - min) + min;
If you want numbers between 0.85 and 1, inclusive, use SplittableRandom and its nextDouble(double origin, double bound) method, or use ThreadLocalRandom and its nextDouble(double origin, double bound) method.
Since the upper bound is exclusive in those methods, you need to bump up the double value by the smallest amount possible, which you can do by calling Math.nextUp(double d).
// Use one of these:
SplittableRandom rnd = new SplittableRandom();
ThreadLocalRandom rnd = ThreadLocalRandom.current();
// Then generate random values like this:
double value = rnd.nextDouble(0.85, Math.nextUp(1d));
For the purpose of providing an alternative, if random has type Random then a single double in the range .85 (inclusive) to 1 (exclusive) would be:
random.doubles(0.85, 1.00).findFirst().getAsDouble();
This is more useful if you wish to generate several values within the range.
Note that an advantage of Random over Math.random is that you can provide a seed which is useful for testing.

Exponential distribution in Java not right - values too small?

I am trying to generate an exponential distribution for arrival and service times of processes. In C++, the example I have works fine and generates pseudo-random numbers in the range [0, inf) and some are bigger as expected. In Java, it does not work. The numbers are orders of magnitude smaller than their C++ equivalents, and I NEVER get any values > 0.99 even though I am using the same formula. In C++ I get 1.xx, or 2.xx etc., but never in Java.
lambda is the average rate of arrival and gets varied from 1 to 30.
I know that rand.nextDouble() gives a value b/w 0 and 1 and from the formula given and answers here on this site, this seems to be a needed component.
I should mention that multiplying my distribution values by 10 gets me much closer to where they need to be and they behave as expected.
In Java:
Random rand = new Random();
// if I multiply x by 10, I get much closer to the distribution I need
// I just don't know why it's off by a factor of 10?!
x = (Math.log(1-rand.nextDouble())/(-lambda));
I have also tried:
x = 0;
while (x == 0)
{
x = (-1/lambda)*log(rand.nextDouble());
}
The C++ code I was given:
// returns a random number between 0 and 1
float urand()
{
return( (float) rand()/RAND_MAX );
}
// returns a random number that follows an exp distribution
float genexp(float lambda)
{
float u,x;
x = 0;
while (x == 0)
{
u = urand();
x = (-1/lambda)*log(u);
}
return(x);
}

Do rounding errors occur also in floats representing integers?

When creating a range of numbers as follows,
float increment = 0.01f;
for (int i = 0; i < 100; i++) {
float value = i * increment;
System.out.println(value);
}
it is clear, that I will end up for some i with values like
0.049999997, which are no exact multiples of 0.01, due to rounding errors.
When I try the same with floats in the range of usual integers, I have never seen the same problem:
float increment = 1.0f; //Still a float but representing an int value
for (int i = 0; i < 100; i++) {
float value = i * increment;
System.out.println(value);
}
One could expect, that this also prints out e.g. 49.999999 instead of 50, which I never saw however.
I am wondering, whether I can rely on that for any value of i and any value of increment, as long as it represents an integer (although its type is float).
And if so, I would be interested in an explanation, why rounding errors can not happen in that case.
Integers in a certain range (about up to one million or so) can be represented exactly as a float. Therefore you don't get rounding errors when you work only with them.
This is because float is based on floating point notation.
In rude words it tries to represent your decimal number as a sum of fractions of power 2.
It means it will try to sum 1/2^n1 + 1/2^n2 + 1/2^n3 .... 1/2^nm until gets closes or exact value that you put.
For example (rude):
0.5 it will represent as 1/2
0.25 it will represent as 1/2²
0.1 it will represent as 1/2^4
but in this case it will mutiply the number by 1.600000023841858 (mantissa) and it will give a number closer but not equal to 1 (1/2^4 x 1.600000023841858 = 0,100000001
Now you can see why after some loops the value changes to nonsense values
For rich detail of how it works read floating points IEEE 754
If you want precision you should use for example a BigDecimal from Java that uses another architecture to represent decimal numbers.
Double has the same problem.
Check this tool to see the repressentation of floating point:
http://www.h-schmidt.net/FloatConverter/IEEE754.html
It doesn't really represent an integer. It's still a float that you're just attempting to add the value 1.0 to. You'll get rounding errors as soon as 1.0 underflows (whenever the exponent gets larger than zero).

Set accuracy of random numbers in java?

I was wondering if I am able to set the accuracy of the random double numbers that I generate.
Random generator = new Random();
double randomIndex = generator.nextDouble()*10000;
That produces the random numbers within 10000.
How am I able to set the accuracy to 4?
A couple of things. First, you mean "precision", not "accuracy". Second, you need to clarify why you want to do this, because a correct answer will depend on it.
If all you want to do is display the numbers with that precision, it is a formatting issue. You can use, e.g. System.out.printf("%.4f", value) or String.format().
If you are trying to to generate numbers with that precision, you could approximate by doing something like (rounding left out for simplicity):
double value = (int)(generateor.nextDouble() * 10000.0) / 10000.0;
Or if you want your range to be 0-10000 instead of 0-1:
double value = (int)(generateor.nextDouble() * 100000000.0) / 10000.0;
Due to the way floating-point numbers are stored, that will not be exact, but perhaps it is close enough for your purposes. If you need exact, you would want to store as integers, e.g.:
int value = (int)(generator.nextDouble() * 10000.0);
Then you can operate on that internally, and display as:
System.out.printf("%.4f", value / 10000.0);
Adjust multiplication factor above if you meant you wanted your range to be 0-10000.
If you are merely trying to generate a number in [0, 10000), you can use Random.nextInt(int) with a range specified, or simply cast the value to an int as above (optionally rounding).
Random generator = new Random();
double randomIndex = generator.nextDouble()*10000;
randomIndex=Math.floor(randomIndex * 10000) / 10000;//this is the trick
If you want 4 digits after the decimal mark you can simply do the following:
Random generator = new Random();
double randomIndex = Math.floor(generator.nextDouble()*10000 * 10000) /10000;
Random generator = new Random();
double randomIndex = Double.parseDouble(new DecimalFormat("##.####")
.format(generator.nextDouble() * 10000));
Simply
double result = generator.nextLong() / 10000.0;
Note, hoewever, that you can never be sure that the number has exactly 4 decimals, whenever you hit a number that is not representable in a double.
Anyway, the requirement is silly, because a double simply does not have decimal positions. Hence, to request 4 of them makes no sense.

Generate random double from random long

in Java I have a random generator that generates random number from -2^63 to 2^63 and that is NOT the java.util.Random.
I need to generate random double in (0,1), this is what I've done so far:
return (seed/(double)(9223372036854775807L))/2+0.5;//seed is a random long
Is this right? Are there any numerical problem (underflow?)?
Could be better/faster?
Thank you.
I would use Math.scalb as the most efficient and ensures there is no funny behaviour due to rounding or representation error
double d = Math.scalb(seed >>> 1, -63);
You can only use 53 bits in a double so some will be discarded.
If you run
long seed = Long.MAX_VALUE;
System.out.println(Math.scalb(seed >>> 1, -63));
prints
0.5
With a seed of 0 you get 0.0
With a seed of -1 you get 1.0
I would prefer to see just a single division.
0.5+(seed/1.84467440737096E+19);
That said, you are going to run up against issues with floating point accuracy since you have 64 random integer bits which you then try to squeeze into 53 bits of double precision. You may be better off making a dedicated generator for floating point values, but I could not say for sure since I don't know your motivation.
The fastest way would probably just be to set the first three bits in your long to 0 and then use those bits to make a double.:
double rand = Double.longBitsToDouble(seed & 0x1FFFFFFFFFFFFFFFL);
This works by forcing the sign to positive, and exponent to be less than 0, which will cause the mantissa to be shifted right at least once. It gives an even distribution assuming all the ints in the long are completely random. Here is a full Java program that uses Random to generate random longs, and then this method to convert them to double's between 0 and 1:
import java.util.Random;
class Main{
public static void main(String[] args){
Random rand = new Random();
long seed = rand.nextLong();
double x = Double.longBitsToDouble(seed & 0x1FFFFFFFFFFFFFFFL);
System.out.println(x);
}
}
This is the output of 10 executions:
1.1211565592484309E-247
8.84224349357039E-242
6.956043405745214E-271
3.747746366809532E-232
9.302628573486166E-158
1.1440116527034282E-166
1.2574577719255876E-198
5.104999671234867E-269
3.360619724894072E-213
1.5654452507283312E-220
Edit
This gives a uniform distribution of all possible doubles between 0 and 1. Since there are many more small doubles you will likely never see a number close to 1. You can fix this by generating a new exponent based on the bits of the existing one, but you need a loop to do it, so it probably isn't the fastest method after factoring this in:
long exponent = 0;
for(int i = 52; (seed >>> i & 1) > 0; i++) exponent++;
double x = Double.longBitsToDouble(seed & 0x000FFFFFFFFFFFFFL | ((1022 - exponent) << 52));
0.4773960377161338
0.929045618651037
0.7183096209363845
0.33962049395497845
0.45568660174922454
0.11670190555677815
0.09371618427480996
0.8192870898479095
0.9365016017283178
0.11311614413193898
Not exactly. I think that easier way is to do the following:
new Random(seed).nextDouble()
Unless I'm misreading your need to have a random double from 0 to 1, Java's built in Math.random does just that. So you could avoid all the conversion you are currently doing.

Categories