• Subject: Re: The probability of returning zero by math.random is doubled.
• From: "Mark Meijer" <meijer78@...>
• Date: Thu, 18 Sep 2008 10:26:22 +0200

```The manual does state that "No guarantees can be given for its
statistical properties." I'm no maths expert, but this seems
especially true if rand can indeed return values greater than
RAND_MAX, as is claimed for SunOS (unless its range is exactly a
multiple of RAND_MAX).

When I looked at the relevant code in lmathlib.c, though, something
else occured to me. For reference, here is that code:

---------->8----------
static int math_random (lua_State *L) {
/* the `%' avoids the (rare) case of r==1, and is needed also because on
some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */
lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX;
switch (lua_gettop(L)) {  /* check number of arguments */
case 0: {  /* no arguments */
lua_pushnumber(L, r);  /* Number between 0 and 1 */
break;
}
case 1: {  /* only upper limit */
int u = luaL_checkint(L, 1);
luaL_argcheck(L, 1<=u, 1, "interval is empty");
lua_pushnumber(L, floor(r*u)+1);  /* int between 1 and `u' */
break;
}
case 2: {  /* lower and upper limits */
int l = luaL_checkint(L, 1);
int u = luaL_checkint(L, 2);
luaL_argcheck(L, l<=u, 2, "interval is empty");
lua_pushnumber(L, floor(r*(u-l+1))+l);  /* int between `l' and `u' */
break;
}
default: return luaL_error(L, "wrong number of arguments");
}
return 1;
}
----------8<----------

Firstly, the range returned by math.random() is stated in the manual
to be in the range [0,1) which (I believe) means  the 1 is exclusive
(i.e. it should never actually return 1). I think in the range from C
rand() the RAND_MAX is inclusive. So, the (original) scaling
calculation done in lmathlib.c tells me math.random() can actually
return 1. OP's patch prevents this, although I don't believe that
patch is an improvement to the statistical properties of the returned
value. I believe the original calculation preserves the statistical
properties of whatever is returned by rand(), except for the SunOS
issue. But again, I'm no maths expert.

Secondly, when arguments are passed to math.random to indicate a
desired range, according to the manual, the returned value is
inclusive of both the min and max value of that range. But due to the
first issue, the returned value could potentially exceed the requested
maximum value, by 1 (i.e. in case 1, if r==1 and u==1 then
floor(1*1)+1 == 2). This is fixed if the range of 'r' is made to be
exclusive of 1. Although, to improve it's statistical properties, the
calculation should rather be "floor(r*u+.5)", I think.

Cheers

2008/9/18 Atry <pop.atry@gmail.com>:
> In math_random(), whether rand() returns 0 or RAND_MAX, the result of
> math_random() would be 0. It should modulo RAND_MAX + 1 instead of
> modulo RAND_MAX.
>
> --- lua-5.1.4/src/lmathlib.c
> +++ lua-5.1.4/src/lmathlib.c
> @@ -181,7 +181,7 @@ static int math_max (lua_State *L) {
>  static int math_random (lua_State *L) {
>   /* the `%' avoids the (rare) case of r==1, and is needed also because on
>      some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */
> -  lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX;
> +  lua_Number r = (lua_Number)(rand()%(RAND_MAX + 1)) /
> (lua_Number)(RAND_MAX + 1);
>   switch (lua_gettop(L)) {  /* check number of arguments */
>     case 0: {  /* no arguments */
>       lua_pushnumber(L, r);  /* Number between 0 and 1 */
>

```

• Follow-Ups:
• References: