[*] (AuRandom) Resolve bad floating point distribution

This commit is contained in:
Reece Wilson 2023-12-01 03:04:26 +00:00
parent 741c7228d3
commit cab8627ffd
3 changed files with 90 additions and 9 deletions

View File

@ -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;

View File

@ -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<AuUInt32>::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<AuUInt32>::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()

View File

@ -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);
};
}