Kaydet (Commit) 68e4de94 authored tarafından Pedro Giffuni's avatar Pedro Giffuni

i121421 - Calc's RAND() behaves poorly on most platforms.

Replace WH2006 PRNG with the well known Mersenne Twister
as implemented in Boost. This actually has some drawbacks
with respect to our previous implementation: we lose the
nifty seeding and instead use the timer which is
non-optimal.

The main reason is the speed: MT is said to be at least
4 times faster.

On the other hand I also moved the WH to the scaddins
module where it is  used by Calc's RANDBETWEEN function.
This way we still have the alternative PRNG and we
drop another use of the unreliable rand from libc. 
üst 194c7ff9
......@@ -40,7 +40,6 @@
#include <unotools/transliterationwrapper.hxx>
#include <rtl/ustring.hxx>
#include <rtl/logfile.hxx>
#include <rtl/random.h>
#include <unicode/uchar.h>
#include "interpre.hxx"
......@@ -72,6 +71,9 @@
#include "doubleref.hxx"
#include "queryparam.hxx"
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_01.hpp>
#include <boost/math/special_functions/acosh.hpp>
#include <boost/math/special_functions/asinh.hpp>
#include <boost/math/special_functions/atanh.hpp>
......@@ -1546,46 +1548,12 @@ void ScInterpreter::ScPi()
void ScInterpreter::ScRandom()
{
RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRandom" );
static sal_Int32 nScRandomIx = 0, nScRandomIy = 0, nScRandomIz = 0, nScRandomIt = 0;
static rtlRandomPool aPool = rtl_random_createPool();
double fScRandomW;
// Seeding for the PRNG: should be good enough but we
// monitor the values to keep things under control.
if (nScRandomIx <= 0)
rtl_random_getBytes(aPool, &nScRandomIx, sizeof(nScRandomIx));
if (nScRandomIy <= 0)
rtl_random_getBytes(aPool, &nScRandomIy, sizeof(nScRandomIy));
if (nScRandomIz <= 0)
rtl_random_getBytes(aPool, &nScRandomIz, sizeof(nScRandomIz));
if (nScRandomIt <= 0)
rtl_random_getBytes(aPool, &nScRandomIt, sizeof(nScRandomIt));
// Basically unmodified algorithm from
// Wichman and Hill, "Generating good pseudo-random numbers",
// December 5, 2005.
nScRandomIx = 11600L * (nScRandomIx % 185127L) - 10379L * (nScRandomIx / 185127L);
nScRandomIy = 47003L * (nScRandomIy % 45688L) - 10479L * (nScRandomIy / 45688L);
nScRandomIz = 23000L * (nScRandomIz % 93368L) - 19423L * (nScRandomIz / 93368L);
nScRandomIt = 33000L * (nScRandomIt % 65075L) - 8123L * (nScRandomIt / 65075L);
if (nScRandomIx < 0)
nScRandomIx += 2147483579L;
if (nScRandomIy < 0)
nScRandomIy += 2147483543L;
if (nScRandomIz < 0)
nScRandomIz += 2147483123L;
if (nScRandomIt < 0)
nScRandomIt += 2147483123L;
fScRandomW = (double)nScRandomIx*0.0000000004656613022697297188506231646486 +
(double)nScRandomIy*0.0000000004656613100759859932486569933169 +
(double)nScRandomIz*0.0000000004656613360968421314794009471615 +
(double)nScRandomIt*0.0000000004656614011489951998100056779817;
PushDouble(fScRandomW - (sal_Int32)fScRandomW);
RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "bst", "ScInterpreter::ScRandom" );
boost::random::mt19937 ScGen(static_cast<unsigned int>(std::time(0)));
static boost::uniform_01<boost::mt19937> ScZeroOne(ScGen);
PushDouble(ScZeroOne());
}
......
......@@ -27,6 +27,7 @@
#include <osl/diagnose.h>
#include <rtl/ustrbuf.hxx>
#include <rtl/math.hxx>
#include <rtl/random.h>
#include <string.h>
#include <tools/resmgr.hxx>
......@@ -808,15 +809,52 @@ double SAL_CALL AnalysisAddIn::getSqrtpi( double fNum ) THROWDEF_RTE_IAE
double SAL_CALL AnalysisAddIn::getRandbetween( double fMin, double fMax ) THROWDEF_RTE_IAE
{
static sal_Int32 nScRandomIx = 0, nScRandomIy = 0, nScRandomIz = 0, nScRandomIt = 0;
static rtlRandomPool aPool = rtl_random_createPool();
double fScRandomW;
fMin = ::rtl::math::round( fMin, 0, rtl_math_RoundingMode_Up );
fMax = ::rtl::math::round( fMax, 0, rtl_math_RoundingMode_Up );
if( fMin > fMax )
THROW_IAE;
// Seeding for the PRNG: should be good enough but we
// monitor the values to keep things under control.
if (nScRandomIx <= 0)
rtl_random_getBytes(aPool, &nScRandomIx, sizeof(nScRandomIx));
if (nScRandomIy <= 0)
rtl_random_getBytes(aPool, &nScRandomIy, sizeof(nScRandomIy));
if (nScRandomIz <= 0)
rtl_random_getBytes(aPool, &nScRandomIz, sizeof(nScRandomIz));
if (nScRandomIt <= 0)
rtl_random_getBytes(aPool, &nScRandomIt, sizeof(nScRandomIt));
// Basically unmodified algorithm from
// Wichman and Hill, "Generating good pseudo-random numbers",
// December 5, 2005.
nScRandomIx = 11600L * (nScRandomIx % 185127L) - 10379L * (nScRandomIx / 185127L);
nScRandomIy = 47003L * (nScRandomIy % 45688L) - 10479L * (nScRandomIy / 45688L);
nScRandomIz = 23000L * (nScRandomIz % 93368L) - 19423L * (nScRandomIz / 93368L);
nScRandomIt = 33000L * (nScRandomIt % 65075L) - 8123L * (nScRandomIt / 65075L);
if (nScRandomIx < 0)
nScRandomIx += 2147483579L;
if (nScRandomIy < 0)
nScRandomIy += 2147483543L;
if (nScRandomIz < 0)
nScRandomIz += 2147483123L;
if (nScRandomIt < 0)
nScRandomIt += 2147483123L;
fScRandomW = (double)nScRandomIx*0.0000000004656613022670 +
(double)nScRandomIy*0.0000000004656613100760 +
(double)nScRandomIz*0.0000000004656613360968 +
(double)nScRandomIt*0.0000000004656614011490;
// fMax -> range
double fRet = fMax - fMin + 1.0;
fRet *= rand();
fRet /= (RAND_MAX + 1.0);
fRet *= fScRandomW - (sal_Int32)fScRandomW ;
fRet += fMin;
fRet = floor( fRet ); // simple floor is sufficient here
RETURN_FINITE( fRet );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment