diff options
-rw-r--r-- | src/corelib/global/qrandom.h | 12 | ||||
-rw-r--r-- | tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp | 50 |
2 files changed, 59 insertions, 3 deletions
diff --git a/src/corelib/global/qrandom.h b/src/corelib/global/qrandom.h index 7f96cd6749..2259f2657a 100644 --- a/src/corelib/global/qrandom.h +++ b/src/corelib/global/qrandom.h @@ -62,8 +62,16 @@ public: static Q_CORE_EXPORT quint64 generate64(); static double generateDouble() { - // use get64() to get enough bits - return double(generate64()) / ((std::numeric_limits<quint64>::max)() + double(1.0)); + // IEEE 754 double precision has: + // 1 bit sign + // 10 bits exponent + // 53 bits mantissa + // In order for our result to be normalized in the range [0, 1), we + // need exactly 53 bits of random data. Use generate64() to get enough. + quint64 x = generate64(); + quint64 limit = Q_UINT64_C(1) << std::numeric_limits<double>::digits; + x >>= std::numeric_limits<quint64>::digits - std::numeric_limits<double>::digits; + return double(x) / double(limit); } static qreal bounded(qreal sup) diff --git a/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp index 2195bf8ec8..d1c0c8e965 100644 --- a/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp +++ b/tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp @@ -97,6 +97,8 @@ private slots: void generateReal_data() { generate32_data(); } void generateReal(); + void qualityReal_data() { generate32_data(); } + void qualityReal(); void seedStdRandomEngines(); void stdUniformIntDistribution_data(); @@ -445,7 +447,7 @@ void tst_QRandomGenerator::generateReal() for (int i = 0; i < 4; ++i) { QVERIFY_3TIMES([] { qreal value = QRandomGenerator::generateDouble(); - return value > 0 && value < 1 && value != RandomValueFP; + return value >= 0 && value < 1 && value != RandomValueFP; }()); } @@ -454,6 +456,52 @@ void tst_QRandomGenerator::generateReal() QVERIFY_3TIMES(QRandomGenerator::generateDouble() != QRandomGenerator::generateDouble()); } +void tst_QRandomGenerator::qualityReal() +{ + QFETCH(uint, control); + setRNGControl(control); + + enum { + SampleSize = 160, + + // Expected value: sample size times proportion of the range: + PerfectOctile = SampleSize / 8, + PerfectHalf = SampleSize / 2, + + // Variance is (1 - proportion of range) * expected; sqrt() for standard deviations. + // Should usually be within twice that and almost never outside four times: + RangeHalf = 25, // floor(4 * sqrt((1 - 0.5) * PerfectHalf)) + RangeOctile = 16 // floor(4 * sqrt((1 - 0.125) * PerfectOctile)) + }; + + double data[SampleSize]; + std::generate(std::begin(data), std::end(data), &QRandomGenerator::generateDouble); + + int aboveHalf = 0; + int belowOneEighth = 0; + int aboveSevenEighths = 0; + for (double x : data) { + aboveHalf += x >= 0.5; + belowOneEighth += x < 0.125; + aboveSevenEighths += x >= 0.875; + + // these are strict requirements + QVERIFY(x >= 0); + QVERIFY(x < 1); + } + + qInfo("Halfway distribution: %.1f - %.1f", 100. * aboveHalf / SampleSize, 100 - 100. * aboveHalf / SampleSize); + qInfo("%.1f below 1/8 (expected 12.5%% ideally)", 100. * belowOneEighth / SampleSize); + qInfo("%.1f above 7/8 (expected 12.5%% ideally)", 100. * aboveSevenEighths / SampleSize); + + QVERIFY(aboveHalf < PerfectHalf + RangeHalf); + QVERIFY(aboveHalf > PerfectHalf - RangeHalf); + QVERIFY(aboveSevenEighths < PerfectOctile + RangeOctile); + QVERIFY(aboveSevenEighths > PerfectOctile - RangeOctile); + QVERIFY(belowOneEighth < PerfectOctile + RangeOctile); + QVERIFY(belowOneEighth > PerfectOctile - RangeOctile); +} + template <typename Engine> void seedStdRandomEngine() { QRandomGenerator rd; |