From 32241715f42b437299282367f39becb0c8602d23 Mon Sep 17 00:00:00 2001 From: Sebastian Wilhelmi Date: Tue, 19 Dec 2000 15:57:53 +0000 Subject: [PATCH] Updated G_RAND_DOUBLE_TRANSFORM to be more accurate. Redid g_rand_double() 2000-12-19 Sebastian Wilhelmi * 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 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. --- ChangeLog | 16 +++ ChangeLog.pre-2-0 | 16 +++ ChangeLog.pre-2-10 | 16 +++ ChangeLog.pre-2-12 | 16 +++ ChangeLog.pre-2-2 | 16 +++ ChangeLog.pre-2-4 | 16 +++ ChangeLog.pre-2-6 | 16 +++ ChangeLog.pre-2-8 | 16 +++ docs/reference/ChangeLog | 7 ++ docs/reference/glib/glib-sections.txt | 2 + docs/reference/glib/tmpl/random_numbers.sgml | 42 ++++--- glib/grand.c | 120 +++++++++---------- glib/grand.h | 28 +++-- grand.c | 120 +++++++++---------- grand.h | 28 +++-- 15 files changed, 317 insertions(+), 158 deletions(-) diff --git a/ChangeLog b/ChangeLog index 85e5cc0b9..05e220563 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,21 @@ 2000-12-19 Sebastian Wilhelmi + * 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 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 implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into the affected files. Now that Alex did that for those two, diff --git a/ChangeLog.pre-2-0 b/ChangeLog.pre-2-0 index 85e5cc0b9..05e220563 100644 --- a/ChangeLog.pre-2-0 +++ b/ChangeLog.pre-2-0 @@ -1,5 +1,21 @@ 2000-12-19 Sebastian Wilhelmi + * 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 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 implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into the affected files. Now that Alex did that for those two, diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 85e5cc0b9..05e220563 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,5 +1,21 @@ 2000-12-19 Sebastian Wilhelmi + * 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 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 implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into the affected files. Now that Alex did that for those two, diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index 85e5cc0b9..05e220563 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,5 +1,21 @@ 2000-12-19 Sebastian Wilhelmi + * 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 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 implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into the affected files. Now that Alex did that for those two, diff --git a/ChangeLog.pre-2-2 b/ChangeLog.pre-2-2 index 85e5cc0b9..05e220563 100644 --- a/ChangeLog.pre-2-2 +++ b/ChangeLog.pre-2-2 @@ -1,5 +1,21 @@ 2000-12-19 Sebastian Wilhelmi + * 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 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 implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into the affected files. Now that Alex did that for those two, diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 85e5cc0b9..05e220563 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,5 +1,21 @@ 2000-12-19 Sebastian Wilhelmi + * 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 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 implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into the affected files. Now that Alex did that for those two, diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 85e5cc0b9..05e220563 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,5 +1,21 @@ 2000-12-19 Sebastian Wilhelmi + * 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 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 implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into the affected files. Now that Alex did that for those two, diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 85e5cc0b9..05e220563 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,5 +1,21 @@ 2000-12-19 Sebastian Wilhelmi + * 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 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 implemented ENABLE_GC_FRIENDLY, I forgot to include config.h into the affected files. Now that Alex did that for those two, diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index 9f02516d7..c24794f73 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,3 +1,10 @@ +2000-12-19 Sebastian Wilhelmi + + * 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 * glib/Makefile.am glib/mainloop-states*: add images diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 2c2a17acb..034086c19 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -1626,11 +1626,13 @@ g_rand_new_with_seed g_rand_new g_rand_free g_rand_set_seed +g_rand_boolean g_rand_int g_rand_int_range g_rand_double g_rand_double_range g_random_set_seed +g_random_boolean g_random_int g_random_int_range g_random_double diff --git a/docs/reference/glib/tmpl/random_numbers.sgml b/docs/reference/glib/tmpl/random_numbers.sgml index c2b34c51f..4282c5243 100644 --- a/docs/reference/glib/tmpl/random_numbers.sgml +++ b/docs/reference/glib/tmpl/random_numbers.sgml @@ -32,14 +32,6 @@ distributed random numbers, whereas for example the yield equally distributed numbers. - -A random binary decision is best implemented by using -if(g_random_int()&(1<<@a)), 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. - - @@ -78,6 +70,16 @@ accessed through the g_rand_* functions. @seed: + + +Return a random #gboolean from @rand. This corresponds to a unbiased +coin toss. + + +@rand: a #GRand. +@Returns: a random #gboolean. + + @@ -89,8 +91,8 @@ accessed through the g_rand_* functions. @rand: -@min: -@max: +@begin: +@end: @Returns: @@ -105,8 +107,8 @@ accessed through the g_rand_* functions. @rand: -@min: -@max: +@begin: +@end: @Returns: @@ -116,6 +118,14 @@ accessed through the g_rand_* functions. @seed: + + +Return a random #gboolean. This corresponds to a unbiased coin toss. + + +@Returns: a random #gboolean. + + @@ -125,8 +135,8 @@ accessed through the g_rand_* functions. -@min: -@max: +@begin: +@end: @Returns: @@ -139,8 +149,8 @@ accessed through the g_rand_* functions. -@min: -@max: +@begin: +@end: @Returns: diff --git a/glib/grand.c b/glib/grand.c index b62257ed2..2a60e5bd5 100644 --- a/glib/grand.c +++ b/glib/grand.c @@ -200,71 +200,61 @@ g_rand_int (GRand* rand) return y; } +/* transform [0..2^32] -> [0..1] */ +#define G_RAND_DOUBLE_TRANSFORM 2.3283064365386962890625e-10 + /** * g_rand_int_range: * @rand: a #GRand. - * @min: lower closed bound of the interval. - * @max: upper open bound of the interval. + * @begin: lower closed bound of the interval. + * @end: upper open bound of the interval. * * 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. **/ 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; - g_return_val_if_fail (rand != NULL, min); - g_return_val_if_fail (max > min, min); + g_return_val_if_fail (rand != NULL, begin); + 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 */ { - /* All tricks doing modulo calculations do not have a good - distribution -> We must use this slower method for maximal - quality, but this method is only good for (max - min) <= 2^16 */ + /* This method, which only calls g_rand_int once is only good + * for (end - begin) <= 2^16, because we only have 32 bits set + * 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); - /* we'd rather use the following, if -lm is allowed later on: - random = (gint32) floor (g_rand_double_range (rand, 0, dist)); */ + random = (gint32) (double_rand * dist); } else { - /* Now it's harder to make it right. We calculate the smallest m, - such that dist < 2 ^ m, then we calculate a random number in - [1..2^32-1] and rightshift it by 32 - m. Then we test, if it - is smaller than dist and if not, get a new number and so - 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); + /* Now we use g_rand_double_range (), which will set 52 bits for + us, so that it is safe to round and still get a decent + distribution */ + random = (gint32) g_rand_double_range (rand, 0, 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: * @rand: a #GRand. @@ -276,25 +266,35 @@ g_rand_int_range (GRand* rand, gint32 min, gint32 max) **/ gdouble 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: * @rand: a #GRand. - * @min: lower closed bound of the interval. - * @max: upper open bound of the interval. + * @begin: lower closed bound of the interval. + * @end: upper open bound of the interval. * * Return the next random #gdouble from @rand equaly distributed over - * the range [@min..@max). + * the range [@begin..@end). * * Return value: A random number. **/ 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: - * @min: lower closed bound of the interval. - * @max: upper open bound of the interval. + * @begin: lower closed bound of the interval. + * @end: upper open bound of the interval. * * Return a random #gint32 equaly distributed over the range - * [@min..@max-1]. + * [@begin..@end-1]. * * Return value: A random number. **/ gint32 -g_random_int_range (gint32 min, gint32 max) +g_random_int_range (gint32 begin, gint32 end) { gint32 result; G_LOCK (global_random); if (!global_random) 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); return result; } @@ -363,22 +363,22 @@ g_random_double (void) /** * g_random_double_range: - * @min: lower closed bound of the interval. - * @max: upper open bound of the interval. + * @begin: lower closed 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. **/ gdouble -g_random_double_range (gdouble min, gdouble max) +g_random_double_range (gdouble begin, gdouble end) { double result; G_LOCK (global_random); if (!global_random) 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); return result; } diff --git a/glib/grand.h b/glib/grand.h index 38f4e5e43..8cbeff6e4 100644 --- a/glib/grand.h +++ b/glib/grand.h @@ -35,11 +35,11 @@ typedef struct _GRand GRand; /* GRand - a good and fast random number generator: Mersenne Twister * 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_range -> [min..max-1] + * int_range -> [begin..end-1] * double -> [0..1) - * double_range -> [min..max) + * double_range -> [begin..end) */ 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, guint32 seed); + +#define g_rand_boolean(rand) (g_rand_int ((rand)) & (1<<15)) + guint32 g_rand_int (GRand *rand); gint32 g_rand_int_range (GRand *rand, - gint32 min, - gint32 max); + gint32 begin, + gint32 end); gdouble g_rand_double (GRand *rand); gdouble g_rand_double_range (GRand *rand, - gdouble min, - gdouble max); + gdouble begin, + gdouble end); void g_random_set_seed (guint32 seed); + +#define g_random_boolean() (g_rand_boolean ((rand))) + guint32 g_random_int (void); -gint32 g_random_int_range (gint32 min, - gint32 max); +gint32 g_random_int_range (gint32 begin, + gint32 end); gdouble g_random_double (void); -gdouble g_random_double_range (gdouble min, - gdouble max); +gdouble g_random_double_range (gdouble begin, + gdouble end); G_END_DECLS diff --git a/grand.c b/grand.c index b62257ed2..2a60e5bd5 100644 --- a/grand.c +++ b/grand.c @@ -200,71 +200,61 @@ g_rand_int (GRand* rand) return y; } +/* transform [0..2^32] -> [0..1] */ +#define G_RAND_DOUBLE_TRANSFORM 2.3283064365386962890625e-10 + /** * g_rand_int_range: * @rand: a #GRand. - * @min: lower closed bound of the interval. - * @max: upper open bound of the interval. + * @begin: lower closed bound of the interval. + * @end: upper open bound of the interval. * * 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. **/ 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; - g_return_val_if_fail (rand != NULL, min); - g_return_val_if_fail (max > min, min); + g_return_val_if_fail (rand != NULL, begin); + 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 */ { - /* All tricks doing modulo calculations do not have a good - distribution -> We must use this slower method for maximal - quality, but this method is only good for (max - min) <= 2^16 */ + /* This method, which only calls g_rand_int once is only good + * for (end - begin) <= 2^16, because we only have 32 bits set + * 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); - /* we'd rather use the following, if -lm is allowed later on: - random = (gint32) floor (g_rand_double_range (rand, 0, dist)); */ + random = (gint32) (double_rand * dist); } else { - /* Now it's harder to make it right. We calculate the smallest m, - such that dist < 2 ^ m, then we calculate a random number in - [1..2^32-1] and rightshift it by 32 - m. Then we test, if it - is smaller than dist and if not, get a new number and so - 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); + /* Now we use g_rand_double_range (), which will set 52 bits for + us, so that it is safe to round and still get a decent + distribution */ + random = (gint32) g_rand_double_range (rand, 0, 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: * @rand: a #GRand. @@ -276,25 +266,35 @@ g_rand_int_range (GRand* rand, gint32 min, gint32 max) **/ gdouble 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: * @rand: a #GRand. - * @min: lower closed bound of the interval. - * @max: upper open bound of the interval. + * @begin: lower closed bound of the interval. + * @end: upper open bound of the interval. * * Return the next random #gdouble from @rand equaly distributed over - * the range [@min..@max). + * the range [@begin..@end). * * Return value: A random number. **/ 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: - * @min: lower closed bound of the interval. - * @max: upper open bound of the interval. + * @begin: lower closed bound of the interval. + * @end: upper open bound of the interval. * * Return a random #gint32 equaly distributed over the range - * [@min..@max-1]. + * [@begin..@end-1]. * * Return value: A random number. **/ gint32 -g_random_int_range (gint32 min, gint32 max) +g_random_int_range (gint32 begin, gint32 end) { gint32 result; G_LOCK (global_random); if (!global_random) 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); return result; } @@ -363,22 +363,22 @@ g_random_double (void) /** * g_random_double_range: - * @min: lower closed bound of the interval. - * @max: upper open bound of the interval. + * @begin: lower closed 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. **/ gdouble -g_random_double_range (gdouble min, gdouble max) +g_random_double_range (gdouble begin, gdouble end) { double result; G_LOCK (global_random); if (!global_random) 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); return result; } diff --git a/grand.h b/grand.h index 38f4e5e43..8cbeff6e4 100644 --- a/grand.h +++ b/grand.h @@ -35,11 +35,11 @@ typedef struct _GRand GRand; /* GRand - a good and fast random number generator: Mersenne Twister * 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_range -> [min..max-1] + * int_range -> [begin..end-1] * double -> [0..1) - * double_range -> [min..max) + * double_range -> [begin..end) */ 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, guint32 seed); + +#define g_rand_boolean(rand) (g_rand_int ((rand)) & (1<<15)) + guint32 g_rand_int (GRand *rand); gint32 g_rand_int_range (GRand *rand, - gint32 min, - gint32 max); + gint32 begin, + gint32 end); gdouble g_rand_double (GRand *rand); gdouble g_rand_double_range (GRand *rand, - gdouble min, - gdouble max); + gdouble begin, + gdouble end); void g_random_set_seed (guint32 seed); + +#define g_random_boolean() (g_rand_boolean ((rand))) + guint32 g_random_int (void); -gint32 g_random_int_range (gint32 min, - gint32 max); +gint32 g_random_int_range (gint32 begin, + gint32 end); gdouble g_random_double (void); -gdouble g_random_double_range (gdouble min, - gdouble max); +gdouble g_random_double_range (gdouble begin, + gdouble end); G_END_DECLS