openfoam/applications/test/Random/Test-Random.C
Mark Olesen b85d0b5cb7 ENH: provide Rand48 as generator in the expected C++11 form
- this removes an OS-specific dependency (eg, drand48_r is not POSIX)
  and allows easier use of other random number generators.

  The Rand48 generator has identical behaviour and period as the
  lrand48() library routine, but holds its own seed and state
  (which makes it re-entrant) and can be combined with other
  random distributions.

  However, when using the modified form to obtain scalar values
  they will not be identical to what drand48() yields.

  This is because drand48() uses the raw 48-bit values to directly
  set the mantissa of an IEEE double where as the newer distribution
  normalizes based on the 32-bit value.

STYLE: simplify code in Random::shuffle and use Swap
2018-04-09 11:08:34 +02:00

184 lines
5.1 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Application
Test-Random
Description
Simple test for sequence of random numbers
\*---------------------------------------------------------------------------*/
#include "Rand48.H"
#include "Random.H"
#include <cstdlib>
#include <iostream>
#include <iomanip>
#define TEST_RAW_IEEE
// Construct a positive double with the 48 random bits distributed over
// its fractional part so the resulting FP number is [0.0,1.0).
//
// As per glibc erand48() implementation
#ifdef TEST_RAW_IEEE
#include <ieee754.h>
double randomFraction(const uint64_t bits)
{
// 48-bit value
unsigned short int xsubi[3];
xsubi[0] = (bits & 0xffff);
xsubi[1] = ((bits >> 16) & 0xffff);
xsubi[2] = ((bits >> 32) & 0xffff);
union ieee754_double temp;
temp.ieee.negative = 0;
temp.ieee.exponent = IEEE754_DOUBLE_BIAS;
temp.ieee.mantissa0 = (xsubi[2] << 4) | (xsubi[1] >> 12);
temp.ieee.mantissa1 = ((xsubi[1] & 0xfff) << 20) | (xsubi[0] << 4);
// The lower 4 bits of mantissa1 are always 0.
return temp.d - 1.0;
}
#endif
using namespace Foam;
// Output with cout instead of Info to keep values unsigned on output
using std::cout;
using std::setw;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
// Allow multiple passes to ensure reset is working properly
const label maxIter = 1;
std::default_random_engine deflt(123456);
std::mt19937 mtwist(123456);
std::uniform_real_distribution<scalar> uniform01;
{
Rand48 rnd(123456);
for (label iter=0; iter < maxIter; ++iter)
{
rnd.seed(123456);
::srand48(123456);
mtwist.seed(123456);
Info<< nl << "32-bit random with seed = 123456" << nl;
cout<< setw(12) << "Rand48()"
<< setw(12) << "lrand48()"
<< setw(12) << "mtwister"
<< setw(12) << "default" << nl;
for (int i=0; i<25; i++)
{
cout<< setw(12) << rnd()
<< setw(12) << long(::lrand48())
<< setw(12) << long(mtwist())
<< setw(12) << long(deflt()) << nl;
}
}
}
{
Random rnd(123456);
Rand48 manual(123456);
mtwist.seed(123456);
deflt.seed(123456);
// Two passes to ensure that reset is working properly
for (label iter=0; iter < maxIter; ++iter)
{
::srand48(123456);
rnd.reset(123456);
manual.seed(123456);
mtwist.seed(123456);
deflt.seed(123456);
cout<< nl << "Random (Rand48) with seed = " << rnd.seed()
<< " interval [0,1000]" << nl;
cout<< setw(12) << "Rand48()"
<< setw(12) << "drand48()";
#ifdef TEST_RAW_IEEE
cout<< setw(12) << "manual";
#endif
cout<< setw(12) << "mtwister";
cout<< nl;
for (int i=0; i<25; i++)
{
cout<< setw(12) << (rnd.sample01<scalar>()*1000)
<< setw(12) << (drand48()*1000);
#ifdef TEST_RAW_IEEE
cout<< setw(12) << (randomFraction(manual.raw())*1000);
#endif
cout<< setw(12) << (uniform01(mtwist)*1000);
cout<< setw(12) << (uniform01(deflt)*1000);
cout<< nl;
}
}
}
{
Rand48 rnd1(123456);
Rand48 rnd2(123456);
::srand48(123456);
rnd2.discard(10);
cout<< nl << "Rand48 - test with offset of 10" << nl;
cout<< setw(12) << "Rand48()"
<< setw(12) << "offset-10"
<< setw(12) << "lrand48()"
<< nl;
for (int i=0; i<25; i++)
{
cout<< setw(12) << (rnd1())
<< setw(12) << (rnd2())
<< setw(12) << long(::lrand48())
<< nl;
}
}
cout<< nl << "Done." << endl;
return 0;
}
// ************************************************************************* //