Skip to content

Commit 51316f9

Browse files
committed
'math.rand()' uses higher bits to produce float value
The call 'math.rand()' converts the higher bits of the internal unsigned integer random to a float, instead of its lower bits. That ensures that Lua compiled with different float precisions always generates equal (up to the available precision) random numbers when given the same seed.
1 parent 46beca5 commit 51316f9

2 files changed

Lines changed: 46 additions & 26 deletions

File tree

lmathlib.c

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -323,14 +323,18 @@ static Rand64 nextrand (Rand64 *state) {
323323

324324
/*
325325
** Convert bits from a random integer into a float in the
326-
** interval [0,1).
326+
** interval [0,1), getting the higher FIG bits from the
327+
** random unsigned integer and converting that to a float.
327328
*/
328-
#define maskFIG (~(~(Rand64)1 << (FIGS - 1))) /* use FIGS bits */
329-
#define shiftFIG \
330-
(l_mathop(0.5) / ((Rand64)1 << (FIGS - 1))) /* 2^(-FIGS) */
329+
330+
/* must throw out the extra (64 - FIGS) bits */
331+
#define shift64_FIG (64 - FIGS)
332+
333+
/* to scale to [0, 1), multiply by scaleFIG = 2^(-FIGS) */
334+
#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1)))
331335

332336
static lua_Number I2d (Rand64 x) {
333-
return (lua_Number)(x & maskFIG) * shiftFIG;
337+
return (lua_Number)(trim64(x) >> shift64_FIG) * scaleFIG;
334338
}
335339

336340
/* convert a 'Rand64' to a 'lua_Unsigned' */
@@ -449,35 +453,49 @@ static Rand64 nextrand (Rand64 *state) {
449453
/* an unsigned 1 with proper type */
450454
#define UONE ((lu_int32)1)
451455

456+
452457
#if FIGS <= 32
453458

454-
#define maskHI 0 /* do not need bits from higher half */
455-
#define maskLOW (~(~UONE << (FIGS - 1))) /* use FIGS bits */
456-
#define shiftFIG (l_mathop(0.5) / (UONE << (FIGS - 1))) /* 2^(-FIGS) */
459+
/* 2^(-FIGS) */
460+
#define scaleFIG (l_mathop(0.5) / (UONE << (FIGS - 1)))
461+
462+
/*
463+
** get up to 32 bits from higher half, shifting right to
464+
** throw out the extra bits.
465+
*/
466+
static lua_Number I2d (Rand64 x) {
467+
lua_Number h = (lua_Number)(trim32(x.h) >> (32 - FIGS));
468+
return h * scaleFIG;
469+
}
457470

458471
#else /* 32 < FIGS <= 64 */
459472

460473
/* must take care to not shift stuff by more than 31 slots */
461474

462-
/* use FIGS - 32 bits from higher half */
463-
#define maskHI (~(~UONE << (FIGS - 33)))
475+
/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */
476+
#define scaleFIG \
477+
((lua_Number)1.0 / (UONE << 30) / 8.0 / (UONE << (FIGS - 33)))
464478

465-
/* use 32 bits from lower half */
466-
#define maskLOW (~(~UONE << 31))
467-
468-
/* 2^(-FIGS) == (1 / 2^33) / 2^(FIGS-33) */
469-
#define shiftFIG ((lua_Number)(1.0 / 8589934592.0) / (UONE << (FIGS - 33)))
479+
/*
480+
** use FIGS - 32 bits from lower half, throwing out the other
481+
** (32 - (FIGS - 32)) = (64 - FIGS) bits
482+
*/
483+
#define shiftLOW (64 - FIGS)
470484

471-
#endif
485+
/*
486+
** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32)
487+
*/
488+
#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * 2.0)
472489

473-
#define twoto32 l_mathop(4294967296.0) /* 2^32 */
474490

475491
static lua_Number I2d (Rand64 x) {
476-
lua_Number h = (lua_Number)(x.h & maskHI);
477-
lua_Number l = (lua_Number)(x.l & maskLOW);
478-
return (h * twoto32 + l) * shiftFIG;
492+
lua_Number h = (lua_Number)trim32(x.h) * shiftHI;
493+
lua_Number l = (lua_Number)(trim32(x.l) >> shiftLOW);
494+
return (h + l) * scaleFIG;
479495
}
480496

497+
#endif
498+
481499

482500
/* convert a 'Rand64' to a 'lua_Unsigned' */
483501
static lua_Unsigned I2UInt (Rand64 x) {

testes/math.lua

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -823,17 +823,19 @@ do
823823
assert(random(0) == res)
824824

825825
math.randomseed(1007, 0)
826-
-- using lower bits to generate random floats; (the '% 2^32' converts
826+
-- using higher bits to generate random floats; (the '% 2^32' converts
827827
-- 32-bit integers to floats as unsigned)
828828
local res
829829
if floatbits <= 32 then
830-
-- get all bits from the lower half
831-
res = (l & ~(~0 << floatbits)) % 2^32
830+
-- get all bits from the higher half
831+
res = (h >> (32 - floatbits)) % 2^32
832832
else
833-
-- get 32 bits from the lower half and the rest from the higher half
834-
res = ((h & ~(~0 << (floatbits - 32))) % 2^32) * 2^32 + (l % 2^32)
833+
-- get 32 bits from the higher half and the rest from the lower half
834+
res = (h % 2^32) * 2^(floatbits - 32) + ((l >> (64 - floatbits)) % 2^32)
835835
end
836-
assert(random() * 2^floatbits == res)
836+
local rand = random()
837+
assert(eq(rand, 0x0.7a7040a5a323c9d6, 2^-floatbits))
838+
assert(rand * 2^floatbits == res)
837839
end
838840

839841
math.randomseed(0, os.time())

0 commit comments

Comments
 (0)