How to Random.getDouble between A and B?

Greetings carbon based bipedal Java gurus,

I'm trying to get a pseudo-random double value between 0.0 and say 0.0000007, INCLUSIVE.

Evidently, There's something wrong with my math. I'm getting random numbers alright, but never exactly 0.0 or 0.0000007.

Please could you have a geezer at my nextDouble() method and the test harness... Can you spot any mistakes? Or is my test just complete bunkum?

package forums;

/*******************************************************************************

XXX XXXXXXXXXXX XX XXX XXXXX XX XXXXXXX

XXXXX XXXXXXX

XXXXX XX XXXX X X

XXX XXX XX XXXX X X

X X XX XXXXXXX X XXX X X X

X X XXXX XX XXXX XX XXXX

X X XXXXXXX XX XXXX XX XX

X XXXXXXXXXXXX

X XXXX XXX XXX XX XXX XXXXXX XXX XXXXX

This program takes about 5 minutes to run with i<Integer.MAX_VALUE

*******************************************************************************/

class Random

extends java.util.Random

implements java.io.Serializable

{

privatestaticfinallong serialVersionUID = 343221;

public Random(){

super();

}

public Random(long seed){

super(seed);

}

publicdouble nextDouble(double low,double high){

double n = high - low;

double i = super.nextDouble() % n;

if (i >< 0) i*=-1.0;

return low + i;

}

// .... other methods omitted for brevity ....

}

publicclass getRandomDoubleTesterator

{

privatestaticfinal Random random =new Random(System.currentTimeMillis());

privatestaticfinalint TIMES = 100000;

privatestaticvoid testGetDouble_InRange()

throws AssertionError

{

String method ="testGetDouble_InRange";

double low = 0.0;

double high = 0.0000001;

int lows = 0, highs = 0;//lows & highs - count hits on the boundaries

//for (int i=0; i<TIMES; i++) {

for (int i=0; i><Integer.MAX_VALUE; i++){

double x = random.nextDouble(low, high);

if(x><low || x>high)thrownew AssertionError("x="+x+" is not between "+low+" and "+high);

if(x==low||x==high){

System.err.println("Bingo x="+x+"!");break;

}

}

}

publicstaticvoid main(String[] args){

try{

testGetDouble_InRange();

}catch (Throwable e){

e.printStackTrace();

}

}

}

Thanking you all in advance,

Keith.

Message was edited by: corlettk

Ooops... the forum software is doing >< this >< again.

if (i >< 0) should be (i less than 0)

and likewise for (int i=0; i<Integer.MAX_VALUE; i++) {

bump>

[5301 byte] By [corlettka] at [2007-11-27 7:55:48]
# 1

isnt this line:

double i = super.nextDouble() % n;

supposed to be

double i = super.nextDouble() * n;

Although 0 would be inclusive, the 0.000007 would not.

If you use modulo, and n is greater than one, then i would always be between 0 and 1. If n=2, then 1->2 would never be returned.

robtafta at 2007-7-12 19:37:16 > top of Java-index,Java Essentials,Java Programming...
# 2

> Greetings carbon based bipedal Java gurus,

> ...

Hi Keith, I'm no guru, but I'll (try to) answer you anyway.

// ...

public double nextDouble(double low, double high) {

double n = high - low;

double i = super.nextDouble() % n;

if (i < 0) i*=-1.0; // <-- this cannot happen!

return low + i;

}

// ...

The high will only be inclusive if i == n, which cannot happen since super.nextDouble() % n returns something between 0.0 (INCL) and n (EXCL).

And the low will be inclusive if i == 0.0, which will only happen if super.nextDouble() returns either 0.0 or n. The chance of that happening is (1/(2^53)) times two (iff n is in the range [0,1>), which is pretty darn slim.

Regards,

Bart.

prometheuzza at 2007-7-12 19:37:16 > top of Java-index,Java Essentials,Java Programming...
# 3
I think this is because of the double modulo operator truncating instead of rounding, i.e. the division to produce the remainder truncates instead of rounding. Try using Math.IEEEremainder(double, double).Out of curiosity, why do you doi*=-1.0;instead ofi = -i;?
dwga at 2007-7-12 19:37:16 > top of Java-index,Java Essentials,Java Programming...
# 4

@rob,

My original motivation for this class was generating various (en masse) test data sets. I wanted to be able to limit the returned values to very small range... say from 0 to one millionth (inclusive), in order to be able to test calculations & algorithms for rounding inaccuracies... my next challenge was a range of random java.math.BigDecimal's

My nextDouble() method was really intended to produce numbers between 0.0 and 1.0 INCLUSIVE.... so, based on my (limited I admit) understanding of the description of the behaviour % operator on floating point numbers in the java language specification at http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.17.3 I still think that the linedouble i = super.nextDouble() % n;

is producing what I want... but I see why you misunderstood my intent, and will add validations that "low" and "high" should both be in the range 0.0 through 1.0.

Thank you for your assistance. I appreciate it.

@prometheuzz,

Thank You... somehow I managed to completely miss the fact that java.util.Random.nextDouble() never produces 1.0, despite the fact that it says so quit clearly and explicitly in the API doco "the range 0.0d (inclusive) to 1.0d (exclusive)" at http://java.sun.com/javase/6/docs/api/java/util/Random.html#nextDouble()

> if (i < 0) i*=-1.0; // <-- this cannot happen!

Indeed cannot happen, and I shall remove the waste of space forthwith. Ta.

> (1/(2^53)) times two (iff n is in the range [0,1>), which is pretty darn slim.

Pretty **** slim indeed, which means my test is bunkum.

Thank you for you're assistance... I dub thee Sir Prometheuzz Guru (esquire) of the Holy order of the Carbon Based Bipedal Java Gurus, and bringer of the fennel.

@dwg

That's easy, mainly coz I didn't think of i = -i;

which is more compact, and actually clearer to read. I would change it to your version, if I wasn't removing the line all together.

Thank You.

So the current state of play is public double nextDouble(double low, double high) {

double n = high - low + A_POOFTEENTH;

double i;

int times = 0;

do {

i = super.nextDouble() % n;

times++;

if (times>5) throw new AssertionError("ChairToKeyboardInterfaceException: Insert new programmer and press any key to continue.");

} while(i > high);

if (times>2) System.err.println("times="+times);

return low + i;

}

But it obviously still leaves something to be desired.

corlettka at 2007-7-12 19:37:16 > top of Java-index,Java Essentials,Java Programming...
# 5

Wouldn't just this do the trick:

public double nextDouble(double low, double high) {

double n = high - low;

double i = super.nextDouble() % n;

return low + i;

}

?

prometheuzza at 2007-7-12 19:37:16 > top of Java-index,Java Essentials,Java Programming...