summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/global/qrandom.h12
-rw-r--r--tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp50
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;