issue #191 #193 backport random number generator from Lua 5.4

lua54-randomgen
Dibyendu Majumdar 4 years ago
parent bf5aa3e3f6
commit a2e0c2fc3d

@ -1,4 +1,4 @@
mkdir nojit64a
cd nojit64a
cmake -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -DCMAKE_BUILD_TYPE=Debug -DLTESTS=ON -G "Visual Studio 15 2017 Win64" ..
cmake -DCMAKE_INSTALL_PREFIX=c:\Software\ravi -DCMAKE_BUILD_TYPE=Debug -DLTESTS=ON ..
cd ..

@ -143,6 +143,10 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
/* push the value used to represent failure/error */
#define luaL_pushfail(L) lua_pushnil(L)
/*
** {======================================================
** Generic Buffer manipulation

@ -421,14 +421,14 @@
@@ LUA_NUMBER is the floating-point type used by Lua.
@@ LUAI_UACNUMBER is the result of a 'default argument promotion'
@@ over a floating number.
@@ l_mathlim(x) corrects limit name 'x' to the proper float type
@@ l_floatatt(x) corrects float attribute 'x' to the proper float type
** by prefixing it with one of FLT/DBL/LDBL.
@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats.
@@ LUA_NUMBER_FMT is the format for writing floats.
@@ lua_number2str converts a float to a string.
@@ l_mathop allows the addition of an 'l' or 'f' to all math operations.
@@ l_floor takes the floor of a float.
@@ lua_str2number converts a decimal numeric string to a number.
@@ lua_str2number converts a decimal numeral to a number.
*/
@ -440,12 +440,13 @@
l_sprintf((s), sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)(n))
/*
@@ lua_numbertointeger converts a float number to an integer, or
** returns 0 if float is not within the range of a lua_Integer.
** (The range comparisons are tricky because of rounding. The tests
** here assume a two-complement representation, where MININTEGER always
** has an exact representation as a float; MAXINTEGER may not have one,
** and therefore its conversion to float may have an ill-defined value.)
@@ lua_numbertointeger converts a float number with an integral value
** to an integer, or returns 0 if float is not within the range of
** a lua_Integer. (The range comparisons are tricky because of
** rounding. The tests here assume a two-complement representation,
** where MININTEGER always has an exact representation as a float;
** MAXINTEGER may not have one, and therefore its conversion to float
** may have an ill-defined value.)
*/
#define lua_numbertointeger(n,p) \
((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \
@ -459,7 +460,7 @@
#define LUA_NUMBER float
#define l_mathlim(n) (FLT_##n)
#define l_floatatt(n) (FLT_##n)
#define LUAI_UACNUMBER double
@ -475,7 +476,7 @@
#define LUA_NUMBER long double
#define l_mathlim(n) (LDBL_##n)
#define l_floatatt(n) (LDBL_##n)
#define LUAI_UACNUMBER long double
@ -490,7 +491,7 @@
#define LUA_NUMBER double
#define l_mathlim(n) (DBL_##n)
#define l_floatatt(n) (DBL_##n)
#define LUAI_UACNUMBER double
@ -515,11 +516,13 @@
@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER.
**
@@ LUAI_UACINT is the result of a 'default argument promotion'
@@ over a lUA_INTEGER.
@@ over a LUA_INTEGER.
@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
@@ LUA_INTEGER_FMT is the format for writing integers.
@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER.
@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER.
@@ LUA_MAXUNSIGNED is the maximum value for a LUA_UNSIGNED.
@@ LUA_UNSIGNEDBITS is the number of bits in a LUA_UNSIGNED.
@@ lua_integer2str converts an integer to a string.
*/
@ -540,6 +543,9 @@
#define LUA_UNSIGNED unsigned LUAI_UACINT
#define LUA_UNSIGNEDBITS (sizeof(LUA_UNSIGNED) * CHAR_BIT)
/* now the variable definitions */
#if LUA_INT_TYPE == LUA_INT_INT /* { int */
@ -550,6 +556,8 @@
#define LUA_MAXINTEGER INT_MAX
#define LUA_MININTEGER INT_MIN
#define LUA_MAXUNSIGNED UINT_MAX
#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */
#define LUA_INTEGER long
@ -558,6 +566,8 @@
#define LUA_MAXINTEGER LONG_MAX
#define LUA_MININTEGER LONG_MIN
#define LUA_MAXUNSIGNED ULONG_MAX
#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */
/* use presence of macro LLONG_MAX as proxy for C99 compliance */
@ -570,6 +580,8 @@
#define LUA_MAXINTEGER LLONG_MAX
#define LUA_MININTEGER LLONG_MIN
#define LUA_MAXUNSIGNED ULLONG_MAX
#elif defined(LUA_USE_WINDOWS) /* }{ */
/* in Windows, can use specific Windows types */
@ -579,6 +591,8 @@
#define LUA_MAXINTEGER _I64_MAX
#define LUA_MININTEGER _I64_MIN
#define LUA_MAXUNSIGNED _UI64_MAX
#else /* }{ */
#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \
@ -613,7 +627,7 @@
/*
@@ lua_strx2number converts an hexadecimal numeric string to a number.
@@ lua_strx2number converts a hexadecimal numeral to a number.
** In C99, 'strtod' does that conversion. Otherwise, you can
** leave 'lua_strx2number' undefined and Lua will provide its own
** implementation.
@ -631,7 +645,7 @@
/*
@@ lua_number2strx converts a float to an hexadecimal numeric string.
@@ lua_number2strx converts a float to a hexadecimal numeral.
** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that.
** Otherwise, you can leave 'lua_number2strx' undefined and Lua will
** provide its own implementation.
@ -677,7 +691,7 @@
/*
@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point).
** Change that if you do not want to use C locales. (Code using this
** macro must include header 'locale.h'.)
** macro must include the header 'locale.h'.)
*/
#if !defined(lua_getlocaledecpoint)
#define lua_getlocaledecpoint() (localeconv()->decimal_point[0])
@ -718,7 +732,7 @@
** {==================================================================
** Macros that affect the API and must be stable (that is, must be the
** same when you compile Lua and when you compile code that links to
** Lua). You probably do not want/need to change them.
** Lua).
** =====================================================================
*/

@ -1,5 +1,5 @@
/*
** $Id: lmathlib.c,v 1.119.1.1 2017/04/19 17:20:42 roberto Exp $
** $Id: lmathlib.c $
** Standard mathematical library
** See Copyright Notice in lua.h
*/
@ -10,8 +10,11 @@
#include "lprefix.h"
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include "lua.h"
@ -23,19 +26,6 @@
#define PI (l_mathop(3.141592653589793238462643383279502884))
#if !defined(l_rand) /* { */
#if defined(LUA_USE_POSIX)
#define l_rand() random()
#define l_srand(x) srandom(x)
#define L_RANDMAX 2147483647 /* (2^31 - 1), following POSIX */
#else
#define l_rand() rand()
#define l_srand(x) srand(x)
#define L_RANDMAX RAND_MAX
#endif
#endif /* } */
static int math_abs (lua_State *L) {
if (lua_isinteger(L, 1)) {
lua_Integer n = lua_tointeger(L, 1);
@ -87,7 +77,7 @@ static int math_toint (lua_State *L) {
lua_pushinteger(L, n);
else {
luaL_checkany(L, 1);
lua_pushnil(L); /* value is not convertible to integer */
luaL_pushfail(L); /* value is not convertible to integer */
}
return 1;
}
@ -239,22 +229,347 @@ static int math_max (lua_State *L) {
return 1;
}
static int math_type (lua_State *L) {
if (lua_type(L, 1) == LUA_TNUMBER)
lua_pushstring(L, (lua_isinteger(L, 1)) ? "integer" : "float");
else {
luaL_checkany(L, 1);
luaL_pushfail(L);
}
return 1;
}
/*
** {==================================================================
** Pseudo-Random Number Generator based on 'xoshiro256**'.
** ===================================================================
*/
/* number of binary digits in the mantissa of a float */
#define FIGS l_floatatt(MANT_DIG)
#if FIGS > 64
/* there are only 64 random bits; use them all */
#undef FIGS
#define FIGS 64
#endif
/*
** LUA_RAND32 forces the use of 32-bit integers in the implementation
** of the PRN generator (mainly for testing).
*/
#if !defined(LUA_RAND32) && !defined(Rand64)
/* try to find an integer type with at least 64 bits */
#if (ULONG_MAX >> 31 >> 31) >= 3
/* 'long' has at least 64 bits */
#define Rand64 unsigned long
#elif !defined(LUA_USE_C89) && defined(LLONG_MAX)
/* there is a 'long long' type (which must have at least 64 bits) */
#define Rand64 unsigned long long
#elif (LUA_MAXUNSIGNED >> 31 >> 31) >= 3
/* 'lua_Integer' has at least 64 bits */
#define Rand64 lua_Unsigned
#endif
#endif
#if defined(Rand64) /* { */
/*
** Standard implementation, using 64-bit integers.
** If 'Rand64' has more than 64 bits, the extra bits do not interfere
** with the 64 initial bits, except in a right shift. Moreover, the
** final result has to discard the extra bits.
*/
/* avoid using extra bits when needed */
#define trim64(x) ((x) & 0xffffffffffffffffu)
/* rotate left 'x' by 'n' bits */
static Rand64 rotl (Rand64 x, int n) {
return (x << n) | (trim64(x) >> (64 - n));
}
static Rand64 nextrand (Rand64 *state) {
Rand64 state0 = state[0];
Rand64 state1 = state[1];
Rand64 state2 = state[2] ^ state0;
Rand64 state3 = state[3] ^ state1;
Rand64 res = rotl(state1 * 5, 7) * 9;
state[0] = state0 ^ state3;
state[1] = state1 ^ state2;
state[2] = state2 ^ (state1 << 17);
state[3] = rotl(state3, 45);
return res;
}
/* must take care to not shift stuff by more than 63 slots */
/*
** Convert bits from a random integer into a float in the
** interval [0,1), getting the higher FIG bits from the
** random unsigned integer and converting that to a float.
*/
/* must throw out the extra (64 - FIGS) bits */
#define shift64_FIG (64 - FIGS)
/* to scale to [0, 1), multiply by scaleFIG = 2^(-FIGS) */
#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1)))
static lua_Number I2d (Rand64 x) {
return (lua_Number)(trim64(x) >> shift64_FIG) * scaleFIG;
}
/* convert a 'Rand64' to a 'lua_Unsigned' */
#define I2UInt(x) ((lua_Unsigned)trim64(x))
/* convert a 'lua_Unsigned' to a 'Rand64' */
#define Int2I(x) ((Rand64)(x))
#else /* no 'Rand64' }{ */
/* get an integer with at least 32 bits */
#if LUAI_IS32INT
typedef unsigned int lu_int32;
#else
typedef unsigned long lu_int32;
#endif
/*
** This function uses 'double' (instead of 'lua_Number') to ensure that
** all bits from 'l_rand' can be represented, and that 'RANDMAX + 1.0'
** will keep full precision (ensuring that 'r' is always less than 1.0.)
** Use two 32-bit integers to represent a 64-bit quantity.
*/
typedef struct Rand64 {
lu_int32 h; /* higher half */
lu_int32 l; /* lower half */
} Rand64;
/*
** If 'lu_int32' has more than 32 bits, the extra bits do not interfere
** with the 32 initial bits, except in a right shift and comparisons.
** Moreover, the final result has to discard the extra bits.
*/
/* avoid using extra bits when needed */
#define trim32(x) ((x) & 0xffffffffu)
/*
** basic operations on 'Rand64' values
*/
/* build a new Rand64 value */
static Rand64 packI (lu_int32 h, lu_int32 l) {
Rand64 result;
result.h = h;
result.l = l;
return result;
}
/* return i << n */
static Rand64 Ishl (Rand64 i, int n) {
lua_assert(n > 0 && n < 32);
return packI((i.h << n) | (trim32(i.l) >> (32 - n)), i.l << n);
}
/* i1 ^= i2 */
static void Ixor (Rand64 *i1, Rand64 i2) {
i1->h ^= i2.h;
i1->l ^= i2.l;
}
/* return i1 + i2 */
static Rand64 Iadd (Rand64 i1, Rand64 i2) {
Rand64 result = packI(i1.h + i2.h, i1.l + i2.l);
if (trim32(result.l) < trim32(i1.l)) /* carry? */
result.h++;
return result;
}
/* return i * 5 */
static Rand64 times5 (Rand64 i) {
return Iadd(Ishl(i, 2), i); /* i * 5 == (i << 2) + i */
}
/* return i * 9 */
static Rand64 times9 (Rand64 i) {
return Iadd(Ishl(i, 3), i); /* i * 9 == (i << 3) + i */
}
/* return 'i' rotated left 'n' bits */
static Rand64 rotl (Rand64 i, int n) {
lua_assert(n > 0 && n < 32);
return packI((i.h << n) | (trim32(i.l) >> (32 - n)),
(trim32(i.h) >> (32 - n)) | (i.l << n));
}
/* for offsets larger than 32, rotate right by 64 - offset */
static Rand64 rotl1 (Rand64 i, int n) {
lua_assert(n > 32 && n < 64);
n = 64 - n;
return packI((trim32(i.h) >> n) | (i.l << (32 - n)),
(i.h << (32 - n)) | (trim32(i.l) >> n));
}
/*
** implementation of 'xoshiro256**' algorithm on 'Rand64' values
*/
static Rand64 nextrand (Rand64 *state) {
Rand64 res = times9(rotl(times5(state[1]), 7));
Rand64 t = Ishl(state[1], 17);
Ixor(&state[2], state[0]);
Ixor(&state[3], state[1]);
Ixor(&state[1], state[2]);
Ixor(&state[0], state[3]);
Ixor(&state[2], t);
state[3] = rotl1(state[3], 45);
return res;
}
/*
** Converts a 'Rand64' into a float.
*/
/* an unsigned 1 with proper type */
#define UONE ((lu_int32)1)
#if FIGS <= 32
/* 2^(-FIGS) */
#define scaleFIG (l_mathop(0.5) / (UONE << (FIGS - 1)))
/*
** get up to 32 bits from higher half, shifting right to
** throw out the extra bits.
*/
static lua_Number I2d (Rand64 x) {
lua_Number h = (lua_Number)(trim32(x.h) >> (32 - FIGS));
return h * scaleFIG;
}
#else /* 32 < FIGS <= 64 */
/* must take care to not shift stuff by more than 31 slots */
/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */
#define scaleFIG \
((lua_Number)1.0 / (UONE << 30) / 8.0 / (UONE << (FIGS - 33)))
/*
** use FIGS - 32 bits from lower half, throwing out the other
** (32 - (FIGS - 32)) = (64 - FIGS) bits
*/
#define shiftLOW (64 - FIGS)
/*
** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32)
*/
#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * 2.0)
static lua_Number I2d (Rand64 x) {
lua_Number h = (lua_Number)trim32(x.h) * shiftHI;
lua_Number l = (lua_Number)(trim32(x.l) >> shiftLOW);
return (h + l) * scaleFIG;
}
#endif
/* convert a 'Rand64' to a 'lua_Unsigned' */
static lua_Unsigned I2UInt (Rand64 x) {
return ((lua_Unsigned)trim32(x.h) << 31 << 1) | (lua_Unsigned)trim32(x.l);
}
/* convert a 'lua_Unsigned' to a 'Rand64' */
static Rand64 Int2I (lua_Unsigned n) {
return packI((lu_int32)(n >> 31 >> 1), (lu_int32)n);
}
#endif /* } */
/*
** A state uses four 'Rand64' values.
*/
typedef struct {
Rand64 s[4];
} RanState;
/*
** Project the random integer 'ran' into the interval [0, n].
** Because 'ran' has 2^B possible values, the projection can only be
** uniform when the size of the interval is a power of 2 (exact
** division). Otherwise, to get a uniform projection into [0, n], we
** first compute 'lim', the smallest Mersenne number not smaller than
** 'n'. We then project 'ran' into the interval [0, lim]. If the result
** is inside [0, n], we are done. Otherwise, we try with another 'ran',
** until we have a result inside the interval.
*/
static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n,
RanState *state) {
if ((n & (n + 1)) == 0) /* is 'n + 1' a power of 2? */
return ran & n; /* no bias */
else {
lua_Unsigned lim = n;
/* compute the smallest (2^b - 1) not smaller than 'n' */
lim |= (lim >> 1);
lim |= (lim >> 2);
lim |= (lim >> 4);
lim |= (lim >> 8);
lim |= (lim >> 16);
#if (LUA_MAXUNSIGNED >> 31) >= 3
lim |= (lim >> 32); /* integer type has more than 32 bits */
#endif
lua_assert((lim & (lim + 1)) == 0 /* 'lim + 1' is a power of 2, */
&& lim >= n /* not smaller than 'n', */
&& (lim >> 1) < n); /* and it is the smallest one */
while ((ran &= lim) > n) /* project 'ran' into [0..lim] */
ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */
return ran;
}
}
static int math_random (lua_State *L) {
lua_Integer low, up;
double r = (double)l_rand() * (1.0 / ((double)L_RANDMAX + 1.0));
lua_Unsigned p;
RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1));
Rand64 rv = nextrand(state->s); /* next pseudo-random value */
switch (lua_gettop(L)) { /* check number of arguments */
case 0: { /* no arguments */
lua_pushnumber(L, (lua_Number)r); /* Number between 0 and 1 */
lua_pushnumber(L, I2d(rv)); /* float between 0 and 1 */
return 1;
}
case 1: { /* only upper limit */
low = 1;
up = luaL_checkinteger(L, 1);
if (up == 0) { /* single 0 as argument? */
lua_pushinteger(L, I2UInt(rv)); /* full random integer */
return 1;
}
break;
}
case 2: { /* lower and upper limits */
@ -266,35 +581,72 @@ static int math_random (lua_State *L) {
}
/* random integer in the interval [low, up] */
luaL_argcheck(L, low <= up, 1, "interval is empty");
luaL_argcheck(L, low >= 0 || up <= LUA_MAXINTEGER + low, 1,
"interval too large");
r *= (double)(up - low) + 1.0;
lua_pushinteger(L, (lua_Integer)r + low);
/* project random integer into the interval [0, up - low] */
p = project(I2UInt(rv), (lua_Unsigned)up - (lua_Unsigned)low, state);
lua_pushinteger(L, p + (lua_Unsigned)low);
return 1;
}
static int math_randomseed (lua_State *L) {
l_srand((unsigned int)(lua_Integer)luaL_checknumber(L, 1));
(void)l_rand(); /* discard first value to avoid undesirable correlations */
return 0;
static void setseed (lua_State *L, Rand64 *state,
lua_Unsigned n1, lua_Unsigned n2) {
int i;
state[0] = Int2I(n1);
state[1] = Int2I(0xff); /* avoid a zero state */
state[2] = Int2I(n2);
state[3] = Int2I(0);
for (i = 0; i < 16; i++)
nextrand(state); /* discard initial values to "spread" seed */
lua_pushinteger(L, n1);
lua_pushinteger(L, n2);
}
static int math_type (lua_State *L) {
if (lua_type(L, 1) == LUA_TNUMBER) {
if (lua_isinteger(L, 1))
lua_pushliteral(L, "integer");
else
lua_pushliteral(L, "float");
/*
** Set a "random" seed. To get some randomness, use the current time
** and the address of 'L' (in case the machine does address space layout
** randomization).
*/
static void randseed (lua_State *L, RanState *state) {
lua_Unsigned seed1 = (lua_Unsigned)time(NULL);
lua_Unsigned seed2 = (lua_Unsigned)(size_t)L;
setseed(L, state->s, seed1, seed2);
}
static int math_randomseed (lua_State *L) {
RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1));
if (lua_isnone(L, 1)) {
randseed(L, state);
}
else {
luaL_checkany(L, 1);
lua_pushnil(L);
lua_Integer n1 = luaL_checkinteger(L, 1);
lua_Integer n2 = luaL_optinteger(L, 2, 0);
setseed(L, state->s, n1, n2);
}
return 1;
return 2; /* return seeds */
}
static const luaL_Reg randfuncs[] = {
{"random", math_random},
{"randomseed", math_randomseed},
{NULL, NULL}
};
/*
** Register the random functions and initialize their state.
*/
static void setrandfunc (lua_State *L) {
RanState *state = (RanState *)lua_newuserdata(L, sizeof(RanState));
randseed(L, state); /* initialize with a "random" seed */
lua_pop(L, 2); /* remove pushed seeds */
luaL_setfuncs(L, randfuncs, 1);
}
/* }================================================================== */
/*
** {==================================================================
@ -367,8 +719,6 @@ static const luaL_Reg mathlib[] = {
{"min", math_min},
{"modf", math_modf},
{"rad", math_rad},
{"random", math_random},
{"randomseed", math_randomseed},
{"sin", math_sin},
{"sqrt", math_sqrt},
{"tan", math_tan},
@ -384,6 +734,8 @@ static const luaL_Reg mathlib[] = {
{"log10", math_log10},
#endif
/* placeholders */
{"random", NULL},
{"randomseed", NULL},
{"pi", NULL},
{"huge", NULL},
{"maxinteger", NULL},
@ -405,6 +757,7 @@ LUAMOD_API int luaopen_math (lua_State *L) {
lua_setfield(L, -2, "maxinteger");
lua_pushinteger(L, LUA_MININTEGER);
lua_setfield(L, -2, "mininteger");
setrandfunc(L);
return 1;
}

@ -14,6 +14,7 @@
#include <float.h>
#include <limits.h>
#include <locale.h>
#include <math.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@ -969,7 +970,7 @@ static int str_gsub (lua_State *L) {
** to nibble boundaries by making what is left after that first digit a
** multiple of 4.
*/
#define L_NBFD ((l_mathlim(MANT_DIG) - 1)%4 + 1)
#define L_NBFD ((l_floatatt(MANT_DIG) - 1)%4 + 1)
/*
@ -1032,13 +1033,23 @@ static int lua_number2strx (lua_State *L, char *buff, int sz,
/*
** Maximum size of each formatted item. This maximum size is produced
** Maximum size for items formatted with '%f'. This size is produced
** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.',
** and '\0') + number of decimal digits to represent maxfloat (which
** is maximum exponent + 1). (99+3+1 then rounded to 120 for "extra
** expenses", such as locale-dependent stuff)
** is maximum exponent + 1). (99+3+1, adding some extra, 110)
*/
#define MAX_ITEMF (110 + l_floatatt(MAX_10_EXP))
/*
** All formats except '%f' do not need that large limit. The other
** float formats use exponents, so that they fit in the 99 limit for
** significant digits; 's' for large strings and 'q' add items directly
** to the buffer; all integer formats also fit in the 99 limit. The
** worst case are floats: they may need 99 significant digits, plus
** '0x', '-', '.', 'e+XXXX', and '\0'. Adding some extra, 120.
*/
#define MAX_ITEM (120 + l_mathlim(MAX_10_EXP))
#define MAX_ITEM 120
/* valid flags in a format specification */
@ -1173,36 +1184,51 @@ static int str_format (lua_State *L) {
luaL_addchar(&b, *strfrmt++); /* %% */
else { /* format item */
char form[MAX_FORMAT]; /* to store the format ('%...') */
char *buff = luaL_prepbuffsize(&b, MAX_ITEM); /* to put formatted item */
int maxitem = MAX_ITEM;
char *buff = luaL_prepbuffsize(&b, maxitem); /* to put formatted item */
int nb = 0; /* number of bytes in added item */
if (++arg > top)
luaL_argerror(L, arg, "no value");
return luaL_argerror(L, arg, "no value");
strfrmt = scanformat(L, strfrmt, form);
switch (*strfrmt++) {
case 'c': {
nb = l_sprintf(buff, MAX_ITEM, form, (int)luaL_checkinteger(L, arg));
nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg));
break;
}
case 'd': case 'i':
case 'o': case 'u': case 'x': case 'X': {
lua_Integer n = luaL_checkinteger(L, arg);
addlenmod(form, LUA_INTEGER_FRMLEN);
nb = l_sprintf(buff, MAX_ITEM, form, (LUAI_UACINT)n);
nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n);
break;
}
case 'a': case 'A':
addlenmod(form, LUA_NUMBER_FRMLEN);
nb = lua_number2strx(L, buff, MAX_ITEM, form,
nb = lua_number2strx(L, buff, maxitem, form,
luaL_checknumber(L, arg));
break;
case 'e': case 'E': case 'f':
case 'g': case 'G': {
case 'f':
maxitem = MAX_ITEMF; /* extra space for '%f' */
buff = luaL_prepbuffsize(&b, maxitem);
/* FALLTHROUGH */
case 'e': case 'E': case 'g': case 'G': {
lua_Number n = luaL_checknumber(L, arg);
addlenmod(form, LUA_NUMBER_FRMLEN);
nb = l_sprintf(buff, MAX_ITEM, form, (LUAI_UACNUMBER)n);
nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
break;
}
case 'p': {
const void *p = lua_topointer(L, arg);
if (p == NULL) { /* avoid calling 'printf' with argument NULL */
p = "(null)"; /* result */
form[strlen(form) - 1] = 's'; /* format it as a string */
}
nb = l_sprintf(buff, maxitem, form, p);
break;
}
case 'q': {
if (form[2] != '\0') /* modifiers? */
return luaL_error(L, "specifier '%%q' cannot have modifiers");
addliteral(L, &b, arg);
break;
}
@ -1218,18 +1244,17 @@ static int str_format (lua_State *L) {
luaL_addvalue(&b); /* keep entire string */
}
else { /* format the string into 'buff' */
nb = l_sprintf(buff, MAX_ITEM, form, s);
nb = l_sprintf(buff, maxitem, form, s);
lua_pop(L, 1); /* remove result from 'luaL_tolstring' */
}
}
break;
}
default: { /* also treat cases 'pnLlh' */
return luaL_error(L, "invalid option '%%%c' to 'format'",
*(strfrmt - 1));
return luaL_error(L, "invalid conversion '%s' to 'format'", form);
}
}
lua_assert(nb < MAX_ITEM);
lua_assert(nb < maxitem);
luaL_addsize(&b, nb);
}
}

@ -42,27 +42,33 @@
/*
** 'l_intfitsf' checks whether a given integer can be converted to a
** float without rounding. Used in comparisons. Left undefined if
** all integers fit in a float precisely.
** 'l_intfitsf' checks whether a given integer is in the range that
** can be converted to a float without rounding. Used in comparisons.
*/
#if !defined(l_intfitsf)
/* number of bits in the mantissa of a float */
#define NBM (l_mathlim(MANT_DIG))
#define NBM (l_floatatt(MANT_DIG))
/*
** Check whether some integers may not fit in a float, that is, whether
** (maxinteger >> NBM) > 0 (that implies (1 << NBM) <= maxinteger).
** (The shifts are done in parts to avoid shifting by more than the size
** Check whether some integers may not fit in a float, testing whether
** (maxinteger >> NBM) > 0. (That implies (1 << NBM) <= maxinteger.)
** (The shifts are done in parts, to avoid shifting by more than the size
** of an integer. In a worst case, NBM == 113 for long double and
** sizeof(integer) == 32.)
** sizeof(long) == 32.)
*/
#if ((((LUA_MAXINTEGER >> (NBM / 4)) >> (NBM / 4)) >> (NBM / 4)) \
>> (NBM - (3 * (NBM / 4)))) > 0
#define l_intfitsf(i) \
(-((lua_Integer)1 << NBM) <= (i) && (i) <= ((lua_Integer)1 << NBM))
/* limit for integers that fit in a float */
#define MAXINTFITSF ((lua_Unsigned)1 << NBM)
/* check whether 'i' is in the interval [-MAXINTFITSF, MAXINTFITSF] */
#define l_intfitsf(i) ((MAXINTFITSF + l_castS2U(i)) <= (2 * MAXINTFITSF))
#else /* all integers fit in a float precisely */
#define l_intfitsf(i) 1
#endif

@ -725,100 +725,220 @@ end
print("testing 'math.random'")
math.randomseed(0)
local random, max, min = math.random, math.max, math.min
local function testnear (val, ref, tol)
return (math.abs(val - ref) < ref * tol)
end
-- low-level!! For the current implementation of random in Lua,
-- the first call after seed 1007 should return 0x7a7040a5a323c9d6
do
-- all computations should work with 32-bit integers
local h = 0x7a7040a5 -- higher half
local l = 0xa323c9d6 -- lower half
math.randomseed(1007)
-- get the low 'intbits' of the 64-bit expected result
local res = (h << 32 | l) & ~(~0 << intbits)
assert(random(0) == res)
math.randomseed(1007, 0)
-- using higher bits to generate random floats; (the '% 2^32' converts
-- 32-bit integers to floats as unsigned)
local res
if floatbits <= 32 then
-- get all bits from the higher half
res = (h >> (32 - floatbits)) % 2^32
else
-- get 32 bits from the higher half and the rest from the lower half
res = (h % 2^32) * 2^(floatbits - 32) + ((l >> (64 - floatbits)) % 2^32)
end
local rand = random()
assert(eq(rand, 0x0.7a7040a5a323c9d6, 2^-floatbits))
assert(rand * 2^floatbits == res)
end
do
-- testing return of 'randomseed'
local x, y = math.randomseed()
local res = math.random(0)
x, y = math.randomseed(x, y) -- should repeat the state
assert(math.random(0) == res)
math.randomseed(x, y) -- again should repeat the state
assert(math.random(0) == res)
-- keep the random seed for following tests
end
do -- test random for floats
local max = -math.huge
local min = math.huge
for i = 0, 20000 do
local t = math.random()
local randbits = math.min(floatbits, 64) -- at most 64 random bits
local mult = 2^randbits -- to make random float into an integral
local counts = {} -- counts for bits
for i = 1, randbits do counts[i] = 0 end
local up = -math.huge
local low = math.huge
local rounds = 100 * randbits -- 100 times for each bit
local totalrounds = 0
::doagain:: -- will repeat test until we get good statistics
for i = 0, rounds do
local t = random()
assert(0 <= t and t < 1)
max = math.max(max, t)
min = math.min(min, t)
if eq(max, 1, 0.001) and eq(min, 0, 0.001) then
goto ok
up = max(up, t)
low = min(low, t)
assert(t * mult % 1 == 0) -- no extra bits
local bit = i % randbits -- bit to be tested
if (t * 2^bit) % 1 >= 0.5 then -- is bit set?
counts[bit + 1] = counts[bit + 1] + 1 -- increment its count
end
end
totalrounds = totalrounds + rounds
if not (eq(up, 1, 0.001) and eq(low, 0, 0.001)) then
goto doagain
end
-- all bit counts should be near 50%
local expected = (totalrounds / randbits / 2)
for i = 1, randbits do
if not testnear(counts[i], expected, 0.10) then
goto doagain
end
end
print(string.format("float random range in %d calls: [%f, %f]",
totalrounds, low, up))
end
do -- test random for full integers
local up = 0
local low = 0
local counts = {} -- counts for bits
for i = 1, intbits do counts[i] = 0 end
local rounds = 100 * intbits -- 100 times for each bit
local totalrounds = 0
::doagain:: -- will repeat test until we get good statistics
for i = 0, rounds do
local t = random(0)
up = max(up, t)
low = min(low, t)
local bit = i % intbits -- bit to be tested
-- increment its count if it is set
counts[bit + 1] = counts[bit + 1] + ((t >> bit) & 1)
end
totalrounds = totalrounds + rounds
local lim = maxint >> 10
if not (maxint - up < lim and low - minint < lim) then
goto doagain
end
-- all bit counts should be near 50%
local expected = (totalrounds / intbits / 2)
for i = 1, intbits do
if not testnear(counts[i], expected, 0.10) then
goto doagain
end
end
-- loop ended without satisfing condition
assert(false)
::ok::
print(string.format(
"integer random range in %d calls: [minint + %.0fppm, maxint - %.0fppm]",
totalrounds, (minint - low) / minint * 1e6,
(maxint - up) / maxint * 1e6))
end
do
local function aux (p, lim) -- test random for small intervals
local x1, x2
if #p == 1 then x1 = 1; x2 = p[1]
else x1 = p[1]; x2 = p[2]
-- test distribution for a dice
local count = {0, 0, 0, 0, 0, 0}
local rep = 200
local totalrep = 0
::doagain::
for i = 1, rep * 6 do
local r = random(6)
count[r] = count[r] + 1
end
totalrep = totalrep + rep
for i = 1, 6 do
if not testnear(count[i], totalrep, 0.05) then
goto doagain
end
end
end
do
local function aux (x1, x2) -- test random for small intervals
local mark = {}; local count = 0 -- to check that all values appeared
for i = 0, lim or 2000 do
local t = math.random(table.unpack(p))
while true do
local t = random(x1, x2)
assert(x1 <= t and t <= x2)
if not mark[t] then -- new value
mark[t] = true
count = count + 1
end
if count == x2 - x1 + 1 then -- all values appeared; OK
goto ok
if count == x2 - x1 + 1 then -- all values appeared; OK
goto ok
end
end
end
-- loop ended without satisfing condition
assert(false)
::ok::
end
aux({-10,0})
aux({6})
aux({-10, 10})
aux({minint, minint})
aux({maxint, maxint})
aux({minint, minint + 9})
aux({maxint - 3, maxint})
aux(-10,0)
aux(1, 6)
aux(1, 2)
aux(1, 13)
aux(1, 31)
aux(1, 32)
aux(1, 33)
aux(-10, 10)
aux(-10,-10) -- unit set
aux(minint, minint) -- unit set
aux(maxint, maxint) -- unit set
aux(minint, minint + 9)
aux(maxint - 3, maxint)
end
do
local function aux(p1, p2) -- test random for large intervals
local max = minint
local min = maxint
local n = 200
local n = 100
local mark = {}; local count = 0 -- to count how many different values
::doagain::
for _ = 1, n do
local t = math.random(p1, p2)
max = math.max(max, t)
min = math.min(min, t)
local t = random(p1, p2)
if not mark[t] then -- new value
assert(p1 <= t and t <= p2)
max = math.max(max, t)
min = math.min(min, t)
mark[t] = true
count = count + 1
end
end
-- at least 80% of values are different
assert(count >= n * 0.8)
if not (count >= n * 0.8) then
goto doagain
end
-- min and max not too far from formal min and max
local diff = (p2 - p1) // 8
assert(min < p1 + diff and max > p2 - diff)
local diff = (p2 - p1) >> 4
if not (min < p1 + diff and max > p2 - diff) then
goto doagain
end
end
aux(0, maxint)
aux(1, maxint)
aux(3, maxint // 3)
aux(minint, -1)
aux(minint // 2, maxint // 2)
aux(minint, maxint)
aux(minint + 1, maxint)
aux(minint, maxint - 1)
aux(0, 1 << (intbits - 5))
end
for i=1,100 do
assert(math.random(maxint) > 0)
assert(math.random(minint, -1) < 0)
end
assert(not pcall(math.random, 1, 2, 3)) -- too many arguments
assert(not pcall(random, 1, 2, 3)) -- too many arguments
-- empty interval
assert(not pcall(math.random, minint + 1, minint))
assert(not pcall(math.random, maxint, maxint - 1))
assert(not pcall(math.random, maxint, minint))
-- interval too large
assert(not pcall(math.random, minint, 0))
assert(not pcall(math.random, -1, maxint))
assert(not pcall(math.random, minint // 2, maxint // 2 + 1))
assert(not pcall(random, minint + 1, minint))
assert(not pcall(random, maxint, maxint - 1))
assert(not pcall(random, maxint, minint))
print('OK')

@ -269,8 +269,8 @@ do print("testing 'format %a %A'")
matchhexa(n)
end
assert(string.find(string.format("%A", 0.0), "^0X0%.?0?P%+?0$"))
assert(string.find(string.format("%a", -0.0), "^%-0x0%.?0?p%+?0$"))
assert(string.find(string.format("%A", 0.0), "^0X0%.?0*P%+?0$"))
assert(string.find(string.format("%a", -0.0), "^%-0x0%.?0*p%+?0$"))
if not _port then -- test inf, -inf, NaN, and -0.0
assert(string.find(string.format("%a", 1/0), "^inf"))
@ -299,7 +299,7 @@ check("%100.3d", "too long")
check("%1"..aux..".3d", "too long")
check("%1.100d", "too long")
check("%10.1"..aux.."004d", "too long")
check("%t", "invalid option")
check("%t", "invalid conversion")
check("%"..aux.."d", "repeated flags")
check("%d %d", "no value")

Loading…
Cancel
Save