Updated G_RAND_DOUBLE_TRANSFORM to be more accurate. Redid g_rand_double()

2000-12-19  Sebastian Wilhelmi  <wilhelmi@ira.uka.de>

	* grand.c: Updated G_RAND_DOUBLE_TRANSFORM to be more
	accurate. Redid g_rand_double() such that it returns 52 bits after
	the point instead of 32 as before. That OTOH requires calling
	g_rand_int() twice. Overhauled g_rand_int_range(), which is easier
	now thanks to the new precision of g_rand_double(). Thanks to
	Sverre Johansen <sj@ifi.uio.no> for the hint.

	* grand.h: Added g_rand_boolean() and g_random_boolean()
	macros. While they could be omitted due to extreme simplicity,
	they make intention clearer in code and are therefore good to have.

	* grand.c, grand.h: Renamed all 'min' and 'max' parameters to'
	begin' and 'end' resp. to avoid making people think, that 'max' is
	included in the interval. 'end' now isn't, whereas 'begin'
	is. That's similar to the use in the STL.

	* glib/glib-sections.txt: Added g_rand_boolean and
	g_random_boolean macros.

	* glib/tmpl/random_numbers.sgml: Updated.
This commit is contained in:
Sebastian Wilhelmi
2000-12-19 15:57:53 +00:00
committed by Sebastian Wilhelmi
parent 2fb47703e2
commit 32241715f4
15 changed files with 317 additions and 158 deletions

View File

@@ -1,5 +1,21 @@
2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de> 2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
* grand.c: Updated G_RAND_DOUBLE_TRANSFORM to be more
accurate. Redid g_rand_double() such that it returns 52 bits after
the point instead of 32 as before. That OTOH requires calling
g_rand_int() twice. Overhauled g_rand_int_range(), which is easier
now thanks to the new precision of g_rand_double(). Thanks to
Sverre Johansen <sj@ifi.uio.no> for the hint.
* grand.h: Added g_rand_boolean() and g_random_boolean()
macros. While they could be omitted due to extreme simplicity,
they make intention clearer in code and are therefore good to have.
* grand.c, grand.h: Renamed all 'min' and 'max' parameters to'
begin' and 'end' resp. to avoid making people think, that 'max' is
included in the interval. 'end' now isn't, whereas 'begin'
is. That's similar to the use in the STL.
* gslist.c, glist.c: Ok, I'm a moron. When I originally * gslist.c, glist.c: Ok, I'm a moron. When I originally
implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into
the affected files. Now that Alex did that for those two, the affected files. Now that Alex did that for those two,

View File

@@ -1,5 +1,21 @@
2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de> 2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
* grand.c: Updated G_RAND_DOUBLE_TRANSFORM to be more
accurate. Redid g_rand_double() such that it returns 52 bits after
the point instead of 32 as before. That OTOH requires calling
g_rand_int() twice. Overhauled g_rand_int_range(), which is easier
now thanks to the new precision of g_rand_double(). Thanks to
Sverre Johansen <sj@ifi.uio.no> for the hint.
* grand.h: Added g_rand_boolean() and g_random_boolean()
macros. While they could be omitted due to extreme simplicity,
they make intention clearer in code and are therefore good to have.
* grand.c, grand.h: Renamed all 'min' and 'max' parameters to'
begin' and 'end' resp. to avoid making people think, that 'max' is
included in the interval. 'end' now isn't, whereas 'begin'
is. That's similar to the use in the STL.
* gslist.c, glist.c: Ok, I'm a moron. When I originally * gslist.c, glist.c: Ok, I'm a moron. When I originally
implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into
the affected files. Now that Alex did that for those two, the affected files. Now that Alex did that for those two,

View File

@@ -1,5 +1,21 @@
2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de> 2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
* grand.c: Updated G_RAND_DOUBLE_TRANSFORM to be more
accurate. Redid g_rand_double() such that it returns 52 bits after
the point instead of 32 as before. That OTOH requires calling
g_rand_int() twice. Overhauled g_rand_int_range(), which is easier
now thanks to the new precision of g_rand_double(). Thanks to
Sverre Johansen <sj@ifi.uio.no> for the hint.
* grand.h: Added g_rand_boolean() and g_random_boolean()
macros. While they could be omitted due to extreme simplicity,
they make intention clearer in code and are therefore good to have.
* grand.c, grand.h: Renamed all 'min' and 'max' parameters to'
begin' and 'end' resp. to avoid making people think, that 'max' is
included in the interval. 'end' now isn't, whereas 'begin'
is. That's similar to the use in the STL.
* gslist.c, glist.c: Ok, I'm a moron. When I originally * gslist.c, glist.c: Ok, I'm a moron. When I originally
implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into
the affected files. Now that Alex did that for those two, the affected files. Now that Alex did that for those two,

View File

@@ -1,5 +1,21 @@
2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de> 2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
* grand.c: Updated G_RAND_DOUBLE_TRANSFORM to be more
accurate. Redid g_rand_double() such that it returns 52 bits after
the point instead of 32 as before. That OTOH requires calling
g_rand_int() twice. Overhauled g_rand_int_range(), which is easier
now thanks to the new precision of g_rand_double(). Thanks to
Sverre Johansen <sj@ifi.uio.no> for the hint.
* grand.h: Added g_rand_boolean() and g_random_boolean()
macros. While they could be omitted due to extreme simplicity,
they make intention clearer in code and are therefore good to have.
* grand.c, grand.h: Renamed all 'min' and 'max' parameters to'
begin' and 'end' resp. to avoid making people think, that 'max' is
included in the interval. 'end' now isn't, whereas 'begin'
is. That's similar to the use in the STL.
* gslist.c, glist.c: Ok, I'm a moron. When I originally * gslist.c, glist.c: Ok, I'm a moron. When I originally
implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into
the affected files. Now that Alex did that for those two, the affected files. Now that Alex did that for those two,

View File

@@ -1,5 +1,21 @@
2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de> 2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
* grand.c: Updated G_RAND_DOUBLE_TRANSFORM to be more
accurate. Redid g_rand_double() such that it returns 52 bits after
the point instead of 32 as before. That OTOH requires calling
g_rand_int() twice. Overhauled g_rand_int_range(), which is easier
now thanks to the new precision of g_rand_double(). Thanks to
Sverre Johansen <sj@ifi.uio.no> for the hint.
* grand.h: Added g_rand_boolean() and g_random_boolean()
macros. While they could be omitted due to extreme simplicity,
they make intention clearer in code and are therefore good to have.
* grand.c, grand.h: Renamed all 'min' and 'max' parameters to'
begin' and 'end' resp. to avoid making people think, that 'max' is
included in the interval. 'end' now isn't, whereas 'begin'
is. That's similar to the use in the STL.
* gslist.c, glist.c: Ok, I'm a moron. When I originally * gslist.c, glist.c: Ok, I'm a moron. When I originally
implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into
the affected files. Now that Alex did that for those two, the affected files. Now that Alex did that for those two,

View File

@@ -1,5 +1,21 @@
2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de> 2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
* grand.c: Updated G_RAND_DOUBLE_TRANSFORM to be more
accurate. Redid g_rand_double() such that it returns 52 bits after
the point instead of 32 as before. That OTOH requires calling
g_rand_int() twice. Overhauled g_rand_int_range(), which is easier
now thanks to the new precision of g_rand_double(). Thanks to
Sverre Johansen <sj@ifi.uio.no> for the hint.
* grand.h: Added g_rand_boolean() and g_random_boolean()
macros. While they could be omitted due to extreme simplicity,
they make intention clearer in code and are therefore good to have.
* grand.c, grand.h: Renamed all 'min' and 'max' parameters to'
begin' and 'end' resp. to avoid making people think, that 'max' is
included in the interval. 'end' now isn't, whereas 'begin'
is. That's similar to the use in the STL.
* gslist.c, glist.c: Ok, I'm a moron. When I originally * gslist.c, glist.c: Ok, I'm a moron. When I originally
implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into
the affected files. Now that Alex did that for those two, the affected files. Now that Alex did that for those two,

View File

@@ -1,5 +1,21 @@
2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de> 2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
* grand.c: Updated G_RAND_DOUBLE_TRANSFORM to be more
accurate. Redid g_rand_double() such that it returns 52 bits after
the point instead of 32 as before. That OTOH requires calling
g_rand_int() twice. Overhauled g_rand_int_range(), which is easier
now thanks to the new precision of g_rand_double(). Thanks to
Sverre Johansen <sj@ifi.uio.no> for the hint.
* grand.h: Added g_rand_boolean() and g_random_boolean()
macros. While they could be omitted due to extreme simplicity,
they make intention clearer in code and are therefore good to have.
* grand.c, grand.h: Renamed all 'min' and 'max' parameters to'
begin' and 'end' resp. to avoid making people think, that 'max' is
included in the interval. 'end' now isn't, whereas 'begin'
is. That's similar to the use in the STL.
* gslist.c, glist.c: Ok, I'm a moron. When I originally * gslist.c, glist.c: Ok, I'm a moron. When I originally
implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into
the affected files. Now that Alex did that for those two, the affected files. Now that Alex did that for those two,

View File

@@ -1,5 +1,21 @@
2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de> 2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
* grand.c: Updated G_RAND_DOUBLE_TRANSFORM to be more
accurate. Redid g_rand_double() such that it returns 52 bits after
the point instead of 32 as before. That OTOH requires calling
g_rand_int() twice. Overhauled g_rand_int_range(), which is easier
now thanks to the new precision of g_rand_double(). Thanks to
Sverre Johansen <sj@ifi.uio.no> for the hint.
* grand.h: Added g_rand_boolean() and g_random_boolean()
macros. While they could be omitted due to extreme simplicity,
they make intention clearer in code and are therefore good to have.
* grand.c, grand.h: Renamed all 'min' and 'max' parameters to'
begin' and 'end' resp. to avoid making people think, that 'max' is
included in the interval. 'end' now isn't, whereas 'begin'
is. That's similar to the use in the STL.
* gslist.c, glist.c: Ok, I'm a moron. When I originally * gslist.c, glist.c: Ok, I'm a moron. When I originally
implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into
the affected files. Now that Alex did that for those two, the affected files. Now that Alex did that for those two,

View File

@@ -1,3 +1,10 @@
2000-12-19 Sebastian Wilhelmi <wilhelmi@ira.uka.de>
* glib/glib-sections.txt: Added g_rand_boolean and
g_random_boolean macros.
* glib/tmpl/random_numbers.sgml: Updated.
Tue Dec 5 15:41:23 2000 Owen Taylor <otaylor@redhat.com> Tue Dec 5 15:41:23 2000 Owen Taylor <otaylor@redhat.com>
* glib/Makefile.am glib/mainloop-states*: add images * glib/Makefile.am glib/mainloop-states*: add images

View File

@@ -1626,11 +1626,13 @@ g_rand_new_with_seed
g_rand_new g_rand_new
g_rand_free g_rand_free
g_rand_set_seed g_rand_set_seed
g_rand_boolean
g_rand_int g_rand_int
g_rand_int_range g_rand_int_range
g_rand_double g_rand_double
g_rand_double_range g_rand_double_range
g_random_set_seed g_random_set_seed
g_random_boolean
g_random_int g_random_int
g_random_int_range g_random_int_range
g_random_double g_random_double

View File

@@ -32,14 +32,6 @@ distributed random numbers, whereas for example the
yield equally distributed numbers. yield equally distributed numbers.
</para> </para>
<para>
A random binary decision is best implemented by using
<literal>if(g_random_int()&amp;(1<<@a))</literal>, where @a can be every
integer constant from 0 to 31. The Mersenne Twister PRNG is said to
produce highly random lower bits too, but it is common not to rely on
that, so choosing @a to be from 4 to 31 might be wise.
</para>
<!-- ##### SECTION See_Also ##### --> <!-- ##### SECTION See_Also ##### -->
<para> <para>
@@ -78,6 +70,16 @@ accessed through the g_rand_* functions.
@seed: @seed:
<!-- ##### MACRO g_rand_boolean ##### -->
<para>
Return a random #gboolean from @rand. This corresponds to a unbiased
coin toss.
</para>
@rand: a #GRand.
@Returns: a random #gboolean.
<!-- ##### FUNCTION g_rand_int ##### --> <!-- ##### FUNCTION g_rand_int ##### -->
@@ -89,8 +91,8 @@ accessed through the g_rand_* functions.
@rand: @rand:
@min: @begin:
@max: @end:
@Returns: @Returns:
@@ -105,8 +107,8 @@ accessed through the g_rand_* functions.
@rand: @rand:
@min: @begin:
@max: @end:
@Returns: @Returns:
@@ -116,6 +118,14 @@ accessed through the g_rand_* functions.
@seed: @seed:
<!-- ##### MACRO g_random_boolean ##### -->
<para>
Return a random #gboolean. This corresponds to a unbiased coin toss.
</para>
@Returns: a random #gboolean.
<!-- ##### FUNCTION g_random_int ##### --> <!-- ##### FUNCTION g_random_int ##### -->
@@ -125,8 +135,8 @@ accessed through the g_rand_* functions.
<!-- ##### FUNCTION g_random_int_range ##### --> <!-- ##### FUNCTION g_random_int_range ##### -->
@min: @begin:
@max: @end:
@Returns: @Returns:
@@ -139,8 +149,8 @@ accessed through the g_rand_* functions.
<!-- ##### FUNCTION g_random_double_range ##### --> <!-- ##### FUNCTION g_random_double_range ##### -->
@min: @begin:
@max: @end:
@Returns: @Returns:

View File

@@ -200,71 +200,61 @@ g_rand_int (GRand* rand)
return y; return y;
} }
/* transform [0..2^32] -> [0..1] */
#define G_RAND_DOUBLE_TRANSFORM 2.3283064365386962890625e-10
/** /**
* g_rand_int_range: * g_rand_int_range:
* @rand: a #GRand. * @rand: a #GRand.
* @min: lower closed bound of the interval. * @begin: lower closed bound of the interval.
* @max: upper open bound of the interval. * @end: upper open bound of the interval.
* *
* Return the next random #gint32 from @rand equaly distributed over * Return the next random #gint32 from @rand equaly distributed over
* the range [@min..@max-1]. * the range [@begin..@end-1].
* *
* Return value: A random number. * Return value: A random number.
**/ **/
gint32 gint32
g_rand_int_range (GRand* rand, gint32 min, gint32 max) g_rand_int_range (GRand* rand, gint32 begin, gint32 end)
{ {
guint32 dist = max - min; guint32 dist = end - begin;
guint32 random; guint32 random;
g_return_val_if_fail (rand != NULL, min); g_return_val_if_fail (rand != NULL, begin);
g_return_val_if_fail (max > min, min); g_return_val_if_fail (end > begin, begin);
/* All tricks doing modulo calculations do not have a perfect
* distribution -> We must use the slower way through gdouble for
* maximal quality. */
if (dist <= 0x10000L) /* 2^16 */ if (dist <= 0x10000L) /* 2^16 */
{ {
/* All tricks doing modulo calculations do not have a good /* This method, which only calls g_rand_int once is only good
distribution -> We must use this slower method for maximal * for (end - begin) <= 2^16, because we only have 32 bits set
quality, but this method is only good for (max - min) <= 2^16 */ * from the one call to g_rand_int (). */
/* we are using (trans + trans * trans), because g_rand_int only
* covers [0..2^32-1] and thus g_rand_int * trans only covers
* [0..1-2^-32], but the biggest double < 1 is 1-2^-52.
*/
gdouble double_rand = g_rand_int (rand) *
(G_RAND_DOUBLE_TRANSFORM +
G_RAND_DOUBLE_TRANSFORM * G_RAND_DOUBLE_TRANSFORM);
random = (gint32) g_rand_double_range (rand, 0, dist); random = (gint32) (double_rand * dist);
/* we'd rather use the following, if -lm is allowed later on:
random = (gint32) floor (g_rand_double_range (rand, 0, dist)); */
} }
else else
{ {
/* Now it's harder to make it right. We calculate the smallest m, /* Now we use g_rand_double_range (), which will set 52 bits for
such that dist < 2 ^ m, then we calculate a random number in us, so that it is safe to round and still get a decent
[1..2^32-1] and rightshift it by 32 - m. Then we test, if it distribution */
is smaller than dist and if not, get a new number and so random = (gint32) g_rand_double_range (rand, 0, dist);
forth until we get a number smaller than dist. We just return
this. */
guint32 border = 0x20000L; /* 2^17 */
guint right_shift = 15; /* 32 - 17 */
if (dist >= 0x80000000) /* in the case of dist > 2^31 our loop
below will be infinite */
{
right_shift = 0;
}
else
{
while (dist >= border)
{
border <<= 1;
right_shift--;
}
}
do
{
random = g_rand_int (rand) >> right_shift;
} while (random >= dist);
} }
return min + random;
return begin + random;
} }
/* transform [0..2^32-1] -> [0..1) */
#define G_RAND_DOUBLE_TRANSFORM 2.3283064365386963e-10
/** /**
* g_rand_double: * g_rand_double:
* @rand: a #GRand. * @rand: a #GRand.
@@ -276,25 +266,35 @@ g_rand_int_range (GRand* rand, gint32 min, gint32 max)
**/ **/
gdouble gdouble
g_rand_double (GRand* rand) g_rand_double (GRand* rand)
{ {
return g_rand_int (rand) * G_RAND_DOUBLE_TRANSFORM; /* We set all 52 bits after the point for this, not only the first
32. Thats why we need two calls to g_rand_int */
gdouble retval = g_rand_int (rand) * G_RAND_DOUBLE_TRANSFORM;
retval = (retval + g_rand_int (rand)) * G_RAND_DOUBLE_TRANSFORM;
/* The following might happen due to very bad rounding luck, but
* actually this should be more than rare, we just try again then */
if (retval >= 1.0)
return g_rand_double (rand);
return retval;
} }
/** /**
* g_rand_double_range: * g_rand_double_range:
* @rand: a #GRand. * @rand: a #GRand.
* @min: lower closed bound of the interval. * @begin: lower closed bound of the interval.
* @max: upper open bound of the interval. * @end: upper open bound of the interval.
* *
* Return the next random #gdouble from @rand equaly distributed over * Return the next random #gdouble from @rand equaly distributed over
* the range [@min..@max). * the range [@begin..@end).
* *
* Return value: A random number. * Return value: A random number.
**/ **/
gdouble gdouble
g_rand_double_range (GRand* rand, gdouble min, gdouble max) g_rand_double_range (GRand* rand, gdouble begin, gdouble end)
{ {
return g_rand_int (rand) * ((max - min) * G_RAND_DOUBLE_TRANSFORM) + min; return g_rand_double (rand) * (end - begin) + begin;
} }
/** /**
@@ -320,23 +320,23 @@ g_random_int (void)
/** /**
* g_random_int_range: * g_random_int_range:
* @min: lower closed bound of the interval. * @begin: lower closed bound of the interval.
* @max: upper open bound of the interval. * @end: upper open bound of the interval.
* *
* Return a random #gint32 equaly distributed over the range * Return a random #gint32 equaly distributed over the range
* [@min..@max-1]. * [@begin..@end-1].
* *
* Return value: A random number. * Return value: A random number.
**/ **/
gint32 gint32
g_random_int_range (gint32 min, gint32 max) g_random_int_range (gint32 begin, gint32 end)
{ {
gint32 result; gint32 result;
G_LOCK (global_random); G_LOCK (global_random);
if (!global_random) if (!global_random)
global_random = g_rand_new (); global_random = g_rand_new ();
result = g_rand_int_range (global_random, min, max); result = g_rand_int_range (global_random, begin, end);
G_UNLOCK (global_random); G_UNLOCK (global_random);
return result; return result;
} }
@@ -363,22 +363,22 @@ g_random_double (void)
/** /**
* g_random_double_range: * g_random_double_range:
* @min: lower closed bound of the interval. * @begin: lower closed bound of the interval.
* @max: upper open bound of the interval. * @end: upper open bound of the interval.
* *
* Return a random #gdouble equaly distributed over the range [@min..@max). * Return a random #gdouble equaly distributed over the range [@begin..@end).
* *
* Return value: A random number. * Return value: A random number.
**/ **/
gdouble gdouble
g_random_double_range (gdouble min, gdouble max) g_random_double_range (gdouble begin, gdouble end)
{ {
double result; double result;
G_LOCK (global_random); G_LOCK (global_random);
if (!global_random) if (!global_random)
global_random = g_rand_new (); global_random = g_rand_new ();
result = g_rand_double_range (global_random, min, max); result = g_rand_double_range (global_random, begin, end);
G_UNLOCK (global_random); G_UNLOCK (global_random);
return result; return result;
} }

View File

@@ -35,11 +35,11 @@ typedef struct _GRand GRand;
/* GRand - a good and fast random number generator: Mersenne Twister /* GRand - a good and fast random number generator: Mersenne Twister
* see http://www.math.keio.ac.jp/~matumoto/emt.html for more info. * see http://www.math.keio.ac.jp/~matumoto/emt.html for more info.
* The range functions return a value in the intervall [min,max). * The range functions return a value in the intervall [begin, end).
* int -> [0..2^32-1] * int -> [0..2^32-1]
* int_range -> [min..max-1] * int_range -> [begin..end-1]
* double -> [0..1) * double -> [0..1)
* double_range -> [min..max) * double_range -> [begin..end)
*/ */
GRand* g_rand_new_with_seed (guint32 seed); GRand* g_rand_new_with_seed (guint32 seed);
@@ -48,22 +48,28 @@ void g_rand_free (GRand *rand);
void g_rand_set_seed (GRand *rand, void g_rand_set_seed (GRand *rand,
guint32 seed); guint32 seed);
#define g_rand_boolean(rand) (g_rand_int ((rand)) & (1<<15))
guint32 g_rand_int (GRand *rand); guint32 g_rand_int (GRand *rand);
gint32 g_rand_int_range (GRand *rand, gint32 g_rand_int_range (GRand *rand,
gint32 min, gint32 begin,
gint32 max); gint32 end);
gdouble g_rand_double (GRand *rand); gdouble g_rand_double (GRand *rand);
gdouble g_rand_double_range (GRand *rand, gdouble g_rand_double_range (GRand *rand,
gdouble min, gdouble begin,
gdouble max); gdouble end);
void g_random_set_seed (guint32 seed); void g_random_set_seed (guint32 seed);
#define g_random_boolean() (g_rand_boolean ((rand)))
guint32 g_random_int (void); guint32 g_random_int (void);
gint32 g_random_int_range (gint32 min, gint32 g_random_int_range (gint32 begin,
gint32 max); gint32 end);
gdouble g_random_double (void); gdouble g_random_double (void);
gdouble g_random_double_range (gdouble min, gdouble g_random_double_range (gdouble begin,
gdouble max); gdouble end);
G_END_DECLS G_END_DECLS

120
grand.c
View File

@@ -200,71 +200,61 @@ g_rand_int (GRand* rand)
return y; return y;
} }
/* transform [0..2^32] -> [0..1] */
#define G_RAND_DOUBLE_TRANSFORM 2.3283064365386962890625e-10
/** /**
* g_rand_int_range: * g_rand_int_range:
* @rand: a #GRand. * @rand: a #GRand.
* @min: lower closed bound of the interval. * @begin: lower closed bound of the interval.
* @max: upper open bound of the interval. * @end: upper open bound of the interval.
* *
* Return the next random #gint32 from @rand equaly distributed over * Return the next random #gint32 from @rand equaly distributed over
* the range [@min..@max-1]. * the range [@begin..@end-1].
* *
* Return value: A random number. * Return value: A random number.
**/ **/
gint32 gint32
g_rand_int_range (GRand* rand, gint32 min, gint32 max) g_rand_int_range (GRand* rand, gint32 begin, gint32 end)
{ {
guint32 dist = max - min; guint32 dist = end - begin;
guint32 random; guint32 random;
g_return_val_if_fail (rand != NULL, min); g_return_val_if_fail (rand != NULL, begin);
g_return_val_if_fail (max > min, min); g_return_val_if_fail (end > begin, begin);
/* All tricks doing modulo calculations do not have a perfect
* distribution -> We must use the slower way through gdouble for
* maximal quality. */
if (dist <= 0x10000L) /* 2^16 */ if (dist <= 0x10000L) /* 2^16 */
{ {
/* All tricks doing modulo calculations do not have a good /* This method, which only calls g_rand_int once is only good
distribution -> We must use this slower method for maximal * for (end - begin) <= 2^16, because we only have 32 bits set
quality, but this method is only good for (max - min) <= 2^16 */ * from the one call to g_rand_int (). */
/* we are using (trans + trans * trans), because g_rand_int only
* covers [0..2^32-1] and thus g_rand_int * trans only covers
* [0..1-2^-32], but the biggest double < 1 is 1-2^-52.
*/
gdouble double_rand = g_rand_int (rand) *
(G_RAND_DOUBLE_TRANSFORM +
G_RAND_DOUBLE_TRANSFORM * G_RAND_DOUBLE_TRANSFORM);
random = (gint32) g_rand_double_range (rand, 0, dist); random = (gint32) (double_rand * dist);
/* we'd rather use the following, if -lm is allowed later on:
random = (gint32) floor (g_rand_double_range (rand, 0, dist)); */
} }
else else
{ {
/* Now it's harder to make it right. We calculate the smallest m, /* Now we use g_rand_double_range (), which will set 52 bits for
such that dist < 2 ^ m, then we calculate a random number in us, so that it is safe to round and still get a decent
[1..2^32-1] and rightshift it by 32 - m. Then we test, if it distribution */
is smaller than dist and if not, get a new number and so random = (gint32) g_rand_double_range (rand, 0, dist);
forth until we get a number smaller than dist. We just return
this. */
guint32 border = 0x20000L; /* 2^17 */
guint right_shift = 15; /* 32 - 17 */
if (dist >= 0x80000000) /* in the case of dist > 2^31 our loop
below will be infinite */
{
right_shift = 0;
}
else
{
while (dist >= border)
{
border <<= 1;
right_shift--;
}
}
do
{
random = g_rand_int (rand) >> right_shift;
} while (random >= dist);
} }
return min + random;
return begin + random;
} }
/* transform [0..2^32-1] -> [0..1) */
#define G_RAND_DOUBLE_TRANSFORM 2.3283064365386963e-10
/** /**
* g_rand_double: * g_rand_double:
* @rand: a #GRand. * @rand: a #GRand.
@@ -276,25 +266,35 @@ g_rand_int_range (GRand* rand, gint32 min, gint32 max)
**/ **/
gdouble gdouble
g_rand_double (GRand* rand) g_rand_double (GRand* rand)
{ {
return g_rand_int (rand) * G_RAND_DOUBLE_TRANSFORM; /* We set all 52 bits after the point for this, not only the first
32. Thats why we need two calls to g_rand_int */
gdouble retval = g_rand_int (rand) * G_RAND_DOUBLE_TRANSFORM;
retval = (retval + g_rand_int (rand)) * G_RAND_DOUBLE_TRANSFORM;
/* The following might happen due to very bad rounding luck, but
* actually this should be more than rare, we just try again then */
if (retval >= 1.0)
return g_rand_double (rand);
return retval;
} }
/** /**
* g_rand_double_range: * g_rand_double_range:
* @rand: a #GRand. * @rand: a #GRand.
* @min: lower closed bound of the interval. * @begin: lower closed bound of the interval.
* @max: upper open bound of the interval. * @end: upper open bound of the interval.
* *
* Return the next random #gdouble from @rand equaly distributed over * Return the next random #gdouble from @rand equaly distributed over
* the range [@min..@max). * the range [@begin..@end).
* *
* Return value: A random number. * Return value: A random number.
**/ **/
gdouble gdouble
g_rand_double_range (GRand* rand, gdouble min, gdouble max) g_rand_double_range (GRand* rand, gdouble begin, gdouble end)
{ {
return g_rand_int (rand) * ((max - min) * G_RAND_DOUBLE_TRANSFORM) + min; return g_rand_double (rand) * (end - begin) + begin;
} }
/** /**
@@ -320,23 +320,23 @@ g_random_int (void)
/** /**
* g_random_int_range: * g_random_int_range:
* @min: lower closed bound of the interval. * @begin: lower closed bound of the interval.
* @max: upper open bound of the interval. * @end: upper open bound of the interval.
* *
* Return a random #gint32 equaly distributed over the range * Return a random #gint32 equaly distributed over the range
* [@min..@max-1]. * [@begin..@end-1].
* *
* Return value: A random number. * Return value: A random number.
**/ **/
gint32 gint32
g_random_int_range (gint32 min, gint32 max) g_random_int_range (gint32 begin, gint32 end)
{ {
gint32 result; gint32 result;
G_LOCK (global_random); G_LOCK (global_random);
if (!global_random) if (!global_random)
global_random = g_rand_new (); global_random = g_rand_new ();
result = g_rand_int_range (global_random, min, max); result = g_rand_int_range (global_random, begin, end);
G_UNLOCK (global_random); G_UNLOCK (global_random);
return result; return result;
} }
@@ -363,22 +363,22 @@ g_random_double (void)
/** /**
* g_random_double_range: * g_random_double_range:
* @min: lower closed bound of the interval. * @begin: lower closed bound of the interval.
* @max: upper open bound of the interval. * @end: upper open bound of the interval.
* *
* Return a random #gdouble equaly distributed over the range [@min..@max). * Return a random #gdouble equaly distributed over the range [@begin..@end).
* *
* Return value: A random number. * Return value: A random number.
**/ **/
gdouble gdouble
g_random_double_range (gdouble min, gdouble max) g_random_double_range (gdouble begin, gdouble end)
{ {
double result; double result;
G_LOCK (global_random); G_LOCK (global_random);
if (!global_random) if (!global_random)
global_random = g_rand_new (); global_random = g_rand_new ();
result = g_rand_double_range (global_random, min, max); result = g_rand_double_range (global_random, begin, end);
G_UNLOCK (global_random); G_UNLOCK (global_random);
return result; return result;
} }

28
grand.h
View File

@@ -35,11 +35,11 @@ typedef struct _GRand GRand;
/* GRand - a good and fast random number generator: Mersenne Twister /* GRand - a good and fast random number generator: Mersenne Twister
* see http://www.math.keio.ac.jp/~matumoto/emt.html for more info. * see http://www.math.keio.ac.jp/~matumoto/emt.html for more info.
* The range functions return a value in the intervall [min,max). * The range functions return a value in the intervall [begin, end).
* int -> [0..2^32-1] * int -> [0..2^32-1]
* int_range -> [min..max-1] * int_range -> [begin..end-1]
* double -> [0..1) * double -> [0..1)
* double_range -> [min..max) * double_range -> [begin..end)
*/ */
GRand* g_rand_new_with_seed (guint32 seed); GRand* g_rand_new_with_seed (guint32 seed);
@@ -48,22 +48,28 @@ void g_rand_free (GRand *rand);
void g_rand_set_seed (GRand *rand, void g_rand_set_seed (GRand *rand,
guint32 seed); guint32 seed);
#define g_rand_boolean(rand) (g_rand_int ((rand)) & (1<<15))
guint32 g_rand_int (GRand *rand); guint32 g_rand_int (GRand *rand);
gint32 g_rand_int_range (GRand *rand, gint32 g_rand_int_range (GRand *rand,
gint32 min, gint32 begin,
gint32 max); gint32 end);
gdouble g_rand_double (GRand *rand); gdouble g_rand_double (GRand *rand);
gdouble g_rand_double_range (GRand *rand, gdouble g_rand_double_range (GRand *rand,
gdouble min, gdouble begin,
gdouble max); gdouble end);
void g_random_set_seed (guint32 seed); void g_random_set_seed (guint32 seed);
#define g_random_boolean() (g_rand_boolean ((rand)))
guint32 g_random_int (void); guint32 g_random_int (void);
gint32 g_random_int_range (gint32 min, gint32 g_random_int_range (gint32 begin,
gint32 max); gint32 end);
gdouble g_random_double (void); gdouble g_random_double (void);
gdouble g_random_double_range (gdouble min, gdouble g_random_double_range (gdouble begin,
gdouble max); gdouble end);
G_END_DECLS G_END_DECLS