Bug 82554 - uniform_real_distribution can generate the upper endpoint
Summary: uniform_real_distribution can generate the upper endpoint
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 8.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-10-14 18:18 UTC by Ben Longbons
Modified: 2025-09-25 19:04 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ben Longbons 2017-10-14 18:18:34 UTC
This is related to #64351 and #63176, for which a totally-bogus fix was applied.

There is actually a defect in the standard: it requires `generate_canonical` to return at *least* `bits` (e.g. 53), but correct operation is only possible when it generates *exactly* `bits` since floating-point math rounds rather than truncating like integer math.

Furthermore, when `std::uniform_real_distribution`'s parameters have different exponents, the difference must be *subtracted* from the number of bits requested (not possible since `bits` is only a template parameter).

I have not considered *all* the details when uniform_real_distribution crosses 0 or its parameters are not exact powers of 2, but the principle of requesting less-than-53 bits certainly still applies.

Consider the following horrible, but valid RNG (for easy testing):

#include <iomanip>
#include <iostream>
#include <limits>
#include <random>


int main()
{
    // seed with -2 instead of -1 because it returns the post-advance state
    // produces -1, 0, 1, 2, ...
    std::linear_congruential_engine<uint64_t, 1, 1, 0> engine(-2ULL);

    std::cout << std::setprecision(std::numeric_limits<double>::max_digits10);

    std::uniform_real_distribution<double> dist(1.0, 2.0);

    for (int i = 0; i < 2; ++i)
        std::cout << dist(engine) << std::endl;
    // just to prove that yes, they are exact, here are the next values
    // the different discards are because 1.0 has a different exponent
    engine.discard(1 << 11);
    std::cout << dist(engine) << std::endl;
    engine.discard(1 << 12);
    std::cout << dist(engine) << std::endl;
}
Comment 1 Ben Longbons 2017-10-14 20:54:48 UTC
Ugh, part of my explanation was wrong: it's not the difference of exponents, it's the number of common bits between the min and the (inclusive) max. That just happens to both be 1 bit when the (exclusive) max is twice the min.
Comment 2 Jonathan Wakely 2017-10-16 00:24:53 UTC
(In reply to Ben Longbons from comment #0)
> This is related to #64351 and #63176, for which a totally-bogus fix was
> applied.

See PR 80137

> There is actually a defect in the standard:

Then shouldn't it be reported elsewhere?
https://cplusplus.github.io/LWG/lwg-active.html#submit_issue
Comment 3 Ben Longbons 2017-10-17 18:08:30 UTC
There is DR2524 for the [0, 1) case. Otherwise, filing bugs there looks really complicated.

I've given you a reproducer. That's as much as I'm capable of.
Comment 4 Jonathan Wakely 2017-10-17 21:02:03 UTC
Almost all of it is saying things you *don't need to do*.

You just need to send an email.