From cab8627ffdd41966d76c12ed32eb804ac45a2e7f Mon Sep 17 00:00:00 2001 From: Jamie Reece Wilson Date: Fri, 1 Dec 2023 03:04:26 +0000 Subject: [PATCH] [*] (AuRandom) Resolve bad floating point distribution --- Include/Aurora/RNG/IRandomDevice.hpp | 4 +- Source/RNG/AuRandomDevice.cpp | 88 ++++++++++++++++++++++++++-- Source/RNG/AuRandomDevice.hpp | 7 ++- 3 files changed, 90 insertions(+), 9 deletions(-) diff --git a/Include/Aurora/RNG/IRandomDevice.hpp b/Include/Aurora/RNG/IRandomDevice.hpp index abf8bd86..0bae5585 100644 --- a/Include/Aurora/RNG/IRandomDevice.hpp +++ b/Include/Aurora/RNG/IRandomDevice.hpp @@ -22,9 +22,9 @@ namespace Aurora::RNG virtual AuUInt32 NextU32() = 0; virtual AuUInt32 NextU32(AuUInt32 uMin, AuUInt32 uMax) = 0; virtual AuUInt64 NextU64() = 0; - virtual AuInt32 NextInt(AuInt32 uMin, AuInt32 uMax) = 0; + virtual AuInt32 NextInt(AuInt32 iMin, AuInt32 iMax) = 0; virtual double NextDecimal() = 0; - virtual float NextNumber(float min, float max) = 0; + virtual double NextNumber(double dMin, double dMax) = 0; virtual AuUInt32 NextIndex(AuUInt32 uCount /* = max + 1*/) = 0; virtual Memory::MemoryViewRead ToSeed() = 0; diff --git a/Source/RNG/AuRandomDevice.cpp b/Source/RNG/AuRandomDevice.cpp index 23eeec39..015c2aba 100644 --- a/Source/RNG/AuRandomDevice.cpp +++ b/Source/RNG/AuRandomDevice.cpp @@ -202,9 +202,87 @@ namespace Aurora::RNG return uMin + uNext; } + // Source: https://stackoverflow.com/a/5016867 + bool RandomDevice::DecGeometric(int x) + { + if (x <= 0) + { + return true; + } + + while (true) + { + auto r = this->NextByte(); + + if (x < 8) + { + return (r & ((1 << x) - 1)) == 0; + } + else if (r != 0) + { + return false; + } + + x -= 8; + } + } + + // Source: https://stackoverflow.com/a/5016867 + double RandomDevice::UniformFloatInRange(double udMin, double udMax) + { + #if defined(AURNG_USE_GARBAGE_DECIMALS) + return (double(this->NextU32()) * (double(1.0) / double(AuNumericLimits::max()))) * udMax; + #else + union + { + double f; + AuUInt64 u; + } convert; + + convert.f = udMin; + auto uABits = convert.u; + convert.f = udMax; + auto uBBits = convert.u; + + auto uMask = uBBits - uABits; + uMask |= uMask >> 1; + uMask |= uMask >> 2; + uMask |= uMask >> 4; + uMask |= uMask >> 8; + uMask |= uMask >> 16; + uMask |= uMask >> 32; + + int bExp {}; + frexp(udMax, &bExp); + + while (true) + { + int iXExp {}; + double x; + + auto uXBits = this->NextU64(); + uXBits &= uMask; + uXBits += uABits; + if (uXBits >= uBBits) + { + continue; + } + + convert.u = uXBits; + x = convert.f; + frexp(x, &iXExp); + + if (DecGeometric(bExp - iXExp)) + { + return x; + } + } + #endif + } + double RandomDevice::NextDecimal() { - return double(NextU32()) * (double(1.0) / double(AuNumericLimits::max())); + return this->UniformFloatInRange(0.0, 1.0); } AuUInt32 RandomDevice::NextIndex(AuUInt32 uCount /* = max + 1*/) @@ -218,11 +296,11 @@ namespace Aurora::RNG } return uNext; } - - float RandomDevice::NextNumber(float fMin, float fMax) + + double RandomDevice::NextNumber(double dMin, double dMax) { - auto fRange = fMax - fMin; - return NextDecimal() * fRange + fMin; + auto dRange = dMin - dMin; + return this->UniformFloatInRange(0.0, dRange) + dMin; } AuMemoryViewRead RandomDevice::ToSeed() diff --git a/Source/RNG/AuRandomDevice.hpp b/Source/RNG/AuRandomDevice.hpp index cc5237bb..898735e6 100644 --- a/Source/RNG/AuRandomDevice.hpp +++ b/Source/RNG/AuRandomDevice.hpp @@ -24,10 +24,10 @@ namespace Aurora::RNG bool NextBoolean() override; AuUInt32 NextU32() override; AuUInt64 NextU64() override; - AuInt32 NextInt(AuInt32 uMin, AuInt32 uMax) override; + AuInt32 NextInt(AuInt32 iMin, AuInt32 iMax) override; AuUInt32 NextU32(AuUInt32 uMin, AuUInt32 uMax) override; double NextDecimal() override; - float NextNumber(float min, float max) override; + double NextNumber(double dMin, double dMax) override; AuUInt32 NextIndex(AuUInt32 uCount /* = max + 1*/) override; AuMemoryViewRead ToSeed() override; @@ -37,5 +37,8 @@ namespace Aurora::RNG private: RandomDef def_; WELLRand fast_; + + bool DecGeometric(int x); + double UniformFloatInRange(double udMin, double udMax); }; } \ No newline at end of file