diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-12-11 11:34:47 -0200 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-12-11 11:34:47 -0200 |
| commit | 51316f9df7aacb54633a3e9b910a070590ac6259 (patch) | |
| tree | 954c50a7b11ace095e8a2ca70e31832230e902cb | |
| parent | 46beca5bed8a7700b18100fe48a78373be5055f9 (diff) | |
| download | lua-51316f9df7aacb54633a3e9b910a070590ac6259.tar.gz lua-51316f9df7aacb54633a3e9b910a070590ac6259.tar.bz2 lua-51316f9df7aacb54633a3e9b910a070590ac6259.zip | |
'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.
| -rw-r--r-- | lmathlib.c | 58 | ||||
| -rw-r--r-- | testes/math.lua | 14 |
2 files changed, 46 insertions, 26 deletions
| @@ -323,14 +323,18 @@ static Rand64 nextrand (Rand64 *state) { | |||
| 323 | 323 | ||
| 324 | /* | 324 | /* |
| 325 | ** Convert bits from a random integer into a float in the | 325 | ** 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. | ||
| 327 | */ | 328 | */ |
| 328 | #define maskFIG (~(~(Rand64)1 << (FIGS - 1))) /* use FIGS bits */ | 329 | |
| 329 | #define shiftFIG \ | 330 | /* must throw out the extra (64 - FIGS) bits */ |
| 330 | (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1))) /* 2^(-FIGS) */ | 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))) | ||
| 331 | 335 | ||
| 332 | static lua_Number I2d (Rand64 x) { | 336 | static lua_Number I2d (Rand64 x) { |
| 333 | return (lua_Number)(x & maskFIG) * shiftFIG; | 337 | return (lua_Number)(trim64(x) >> shift64_FIG) * scaleFIG; |
| 334 | } | 338 | } |
| 335 | 339 | ||
| 336 | /* convert a 'Rand64' to a 'lua_Unsigned' */ | 340 | /* convert a 'Rand64' to a 'lua_Unsigned' */ |
| @@ -449,35 +453,49 @@ static Rand64 nextrand (Rand64 *state) { | |||
| 449 | /* an unsigned 1 with proper type */ | 453 | /* an unsigned 1 with proper type */ |
| 450 | #define UONE ((lu_int32)1) | 454 | #define UONE ((lu_int32)1) |
| 451 | 455 | ||
| 456 | |||
| 452 | #if FIGS <= 32 | 457 | #if FIGS <= 32 |
| 453 | 458 | ||
| 454 | #define maskHI 0 /* do not need bits from higher half */ | 459 | /* 2^(-FIGS) */ |
| 455 | #define maskLOW (~(~UONE << (FIGS - 1))) /* use FIGS bits */ | 460 | #define scaleFIG (l_mathop(0.5) / (UONE << (FIGS - 1))) |
| 456 | #define shiftFIG (l_mathop(0.5) / (UONE << (FIGS - 1))) /* 2^(-FIGS) */ | 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 | } | ||
| 457 | 470 | ||
| 458 | #else /* 32 < FIGS <= 64 */ | 471 | #else /* 32 < FIGS <= 64 */ |
| 459 | 472 | ||
| 460 | /* must take care to not shift stuff by more than 31 slots */ | 473 | /* must take care to not shift stuff by more than 31 slots */ |
| 461 | 474 | ||
| 462 | /* use FIGS - 32 bits from higher half */ | 475 | /* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */ |
| 463 | #define maskHI (~(~UONE << (FIGS - 33))) | 476 | #define scaleFIG \ |
| 477 | ((lua_Number)1.0 / (UONE << 30) / 8.0 / (UONE << (FIGS - 33))) | ||
| 464 | 478 | ||
| 465 | /* use 32 bits from lower half */ | 479 | /* |
| 466 | #define maskLOW (~(~UONE << 31)) | 480 | ** use FIGS - 32 bits from lower half, throwing out the other |
| 467 | 481 | ** (32 - (FIGS - 32)) = (64 - FIGS) bits | |
| 468 | /* 2^(-FIGS) == (1 / 2^33) / 2^(FIGS-33) */ | 482 | */ |
| 469 | #define shiftFIG ((lua_Number)(1.0 / 8589934592.0) / (UONE << (FIGS - 33))) | 483 | #define shiftLOW (64 - FIGS) |
| 470 | 484 | ||
| 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) | ||
| 472 | 489 | ||
| 473 | #define twoto32 l_mathop(4294967296.0) /* 2^32 */ | ||
| 474 | 490 | ||
| 475 | static lua_Number I2d (Rand64 x) { | 491 | static lua_Number I2d (Rand64 x) { |
| 476 | lua_Number h = (lua_Number)(x.h & maskHI); | 492 | lua_Number h = (lua_Number)trim32(x.h) * shiftHI; |
| 477 | lua_Number l = (lua_Number)(x.l & maskLOW); | 493 | lua_Number l = (lua_Number)(trim32(x.l) >> shiftLOW); |
| 478 | return (h * twoto32 + l) * shiftFIG; | 494 | return (h + l) * scaleFIG; |
| 479 | } | 495 | } |
| 480 | 496 | ||
| 497 | #endif | ||
| 498 | |||
| 481 | 499 | ||
| 482 | /* convert a 'Rand64' to a 'lua_Unsigned' */ | 500 | /* convert a 'Rand64' to a 'lua_Unsigned' */ |
| 483 | static lua_Unsigned I2UInt (Rand64 x) { | 501 | static lua_Unsigned I2UInt (Rand64 x) { |
diff --git a/testes/math.lua b/testes/math.lua index 7c780e59..dc5b84f6 100644 --- a/testes/math.lua +++ b/testes/math.lua | |||
| @@ -823,17 +823,19 @@ do | |||
| 823 | assert(random(0) == res) | 823 | assert(random(0) == res) |
| 824 | 824 | ||
| 825 | math.randomseed(1007, 0) | 825 | 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 |
| 827 | -- 32-bit integers to floats as unsigned) | 827 | -- 32-bit integers to floats as unsigned) |
| 828 | local res | 828 | local res |
| 829 | if floatbits <= 32 then | 829 | if floatbits <= 32 then |
| 830 | -- get all bits from the lower half | 830 | -- get all bits from the higher half |
| 831 | res = (l & ~(~0 << floatbits)) % 2^32 | 831 | res = (h >> (32 - floatbits)) % 2^32 |
| 832 | else | 832 | else |
| 833 | -- get 32 bits from the lower half and the rest from the higher half | 833 | -- get 32 bits from the higher half and the rest from the lower half |
| 834 | res = ((h & ~(~0 << (floatbits - 32))) % 2^32) * 2^32 + (l % 2^32) | 834 | res = (h % 2^32) * 2^(floatbits - 32) + ((l >> (64 - floatbits)) % 2^32) |
| 835 | end | 835 | 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) | ||
| 837 | end | 839 | end |
| 838 | 840 | ||
| 839 | math.randomseed(0, os.time()) | 841 | math.randomseed(0, os.time()) |
