-
Notifications
You must be signed in to change notification settings - Fork 12.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
uniform_real_distribution produces variates in its range _inclusive_ rather than exclusive. #19141
Comments
There is an inconsistency between uniform_int_distribution and uniform_real_distribution: §26.5.8.2.1p1 says: | A uniform_int_distribution random number distribution produces random And §26.5.8.2.2p1 says: | A uniform_real_distribution random number distribution produces random However this clashes with the requirements for uniform_real_distribution's constructor: | Requires: a ≤ b If a == b then it's impossible for uniform_real_distribution to produce a number that satisfies a ≤ x < b, see DR 2168: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3893.html#2168 I'm guessing all the standard libraries you mention assume that §26.5.8.2.2p1 means <= instead of <. |
I don't think this is due to a misunderstanding of the spec by implementors. I think it is due to a misunderstanding of how floating point round off can cause an unintended result. The libc++ implementation is:
And I believe a fix is simply:
|
fix |
Howard, I don't understand how your proposed patch (or mathematical explanation) would deal with Seth's original program modified in light of Jonathan's observation. Consider:
This program obviously fails the assertion with current libc++. But after your proposed patch, the first call to It looks to me as if Jonathan's observation is valid and the Standard has a defect in this area; it doesn't make sense to patch libc++ until the defect has been filed and resolved one way or the other. |
Thanks Arthur. I think it would be a good idea for a debug mode uniform_real_distribution make noise (assert, whatever) if a == b. The uniform_real_distribution requirement of: a <= b is just a silly type-o in the standard. It should read: a < b and libc++ should just assume that the standard says that. In Seth's test case: a < b. My patch causes Seth's test to turn from failing to passing. The case you and Jonathan are discussing is a different case than the one Seth is demonstrating. |
Hmmm… One reasonable response to a == b, even in release mode, is to return a nan. Thoughts on that? |
If it is desired that illegal limits result in a nan being returned from the
The if statement is carefully crafted such that if |
I agree with Howard that the spec makes sense as-is and is not a defect: a must be less than b and the behavior is undefined otherwise, and generally this asymmetry between integral uniform distributions and real uniform distributions is desired. I think a debug mode assertion and an infinite loop otherwise is fine. |
@Howard: I still think the appropriate "fix" (IMHO) is to release-note that the behavior of uniform_real_distribution is the subject of a Defect Report, and then wait for the DR to be resolved. IMHO libc++'s current behavior is strictly better than any of the conceivable alternatives. Nitpick on your patch: is it guaranteed that "__r >= __p.b()" must be meaningful for a user-defined _RealType, or should you rewrite that as "!(__r < __p.b())"? Nitpick on returning NaN: some platforms may not support NaN, and on those that do you'll have to decide what kind of NaN to return (signaling, non-signaling...) IMHO the least bad of the terrible alternatives is to simply return "__p.a()" in that case, which of course is what my preferred implementation (current ToT) already does. :) |
Clarification: Seth wrote:
The spec doesn't actually say this, but it should. The spec is defective. Quoting N3797: In 26.5.8.2.2 [rand.dist.uni.real]/p1: A uniform_real_distribution random number distribution produces random numbers x, a <= x < b, which is good. But then in p2 says: Requires: a <= b which is bad. It should say in p2: Requires: a < b This seems like an obvious defect to me with only one way to correct it. It seems so obvious to me that my recommendation is for libc++ to go ahead and assume that p2 is corrected to: Requires: a < b |
This bug report is not about the case that a == b. The defect you are referring to is only about the case that a == b.
26.5.4.4 [rand.req.genl]/p1/bd says:
So I think "__r >= __p.b()" is fine.
To the best of my knowledge, libc++ is not supporting such a platform, nor claiming to be 100% portable to all platforms.
In my patch I chose non-signaling. A signaling nan is nothing but a 25 year old bug in IEC 60559. They are not useful, unless non-portably transformed into exceptions. The idea for them (exceptions) was useful, just before its time.
Ok, but this does not conform to [rand.dist.uni.real]/p1, a part of the current spec which is not broken. That being said, if p2 is fixed, this result will be undefined behavior, so libc++ can do anything it wants. |
Clarification for all: Two distinct issues are being discussed here, and I get the feeling that nobody is aware of this. Issue 1: When a < b, but b is just a tiny bit bigger than a, libc++ current returns results greater than or equal to b, which violates [rand.dist.uni.real]/p1. Issue 2: What should happen when !(a < b)? This is a real issue, but it is not the issue reported in this bug report. Complicating this question is a defect in the standard that says it is ok for a == b. |
@Howard: The Standard has
We agree that this is an "obvious defect". In your opinion the best way to correct it is to s/<=/</ in p2. The advantages of s/</<=/ in p1 are:
The advantages of s/<=/</ in p2 are:
Perhaps you or Seth could speak to WHY the "asymmetry between integral uniform distributions and real uniform distributions is desired," because I'm not getting it. |
Ah! I did not realize this, thank you for clarifying. Note that no matter which way this is decided in the standard, Seth's test case is not impacted. In this test case a < b. And so no matter what the committee decides for the case a == b should not impact how we address this bug report. I've asked Walter Brown to comment on this issue. He is the lead author of N1932, which first proposed uniform_real_distribution in 2006. |
Aha. We're getting closer and closer to being on the same page. ;)
You're still missing one point: If the defect is corrected by adopting "my" fix (s/</<=/ in p1), then Seth will have no bug report at all; libc++ ALREADY IMPLEMENTS that behavior! Seth was effectively complaining that libc++ had (accidentally) jumped the gun in implementing "my" fix before the DR had been addressed by the Committee.
Excellent. Walter should be able to shed much more light on the mathematical intent of uniform_real_distribution. Interestingly, the original draft of N1932 had the same uniform_int_distribution as C++11, but its uniform_real_distribution had the precondition "a <= b" (same as C++11) and the postcondition "a < x < b" (different from C++11's "a <= x < b"). [So we still have two competing philosophies: In my view, C++11 corrected N1932's "a < x" to "a <= x" but forgot to correct "x < b" to "x <= b". In your view, C++11 corrected N1932's "a < x" to "a <= x" but forgot to correct "a <= b" to "a < b".] |
Well, p2 only says that a must be <= b, but it seems to me the spec fails to define the behavior when a==b because the equation defining the behavior in p1, p(x | a, b) = 1/(b − a), is undefined when a==b. So I think even in the current spec the behavior is undefined when a==b. But I do agree it is a defect that p2 doesn't explicitly disallow a==b as well. About your earlier comment:
I'm attaching a program that seems to contradict this. I wonder if you could take a look and see if perhaps there's something wrong with my test? |
Walter Brown asked me to post this comment on his behalf: Thank you for inviting me to comment on the LWG issue associated with this bug report. Alas I seem currently unable to login to the bug tracker, so I hope you will be able to copy-and-paste the entirety of this note on my behalf; I apologize in advance for its length. I have spent several hours now refreshing my memory by rereading substantial portions of the 20+ papers I (and others) wrote while In brief, I am unconvinced that there is any "defect" or "type-o" in the C++11 specification of uniform_real_distribution, much less any "obvious" or "silly" one. Please permit me to elaborate. The most recent change to the specification of uniform_real_distribution seems to have been in 2006. Prior to then, the requirement on the Please note that under no circumstances would we ever, ever, ever consider having a closed interval here. Sadly, the bug report mostly quotes the C++ standard out of context on this point, as did the LWG issue report, failing to cite the adjoining mathematics that carefully specifies the requirement on the associated probability density function, namely that it have the constant value given by [FWIW, my guidance to implementors here is to follow your existing/prevailing policy re users who violate a documented precondition: You can terminate(), or assert() first, or throw, or enter an infinite loop, or return a predetermined out-of-bandwidth value, or do whatever. But such actions are IMO at best a courtesy to users; as we all know, the C++ standard does not specify what happens when a precondition is violated.] However, we don't need the same strict It can, of course, be argued that an object having
I see no reason to forbid such code, as it seems perfectly well-formed, well-specified, well-behaved, and incredibly useful in applications whose distribution's parameters are in continual flux. Moreover, I see no inconsistency between the respective specified preconditions for constructing and invoking such a distribution object. I intend to recommend, when the LWG issue is raised (likely later this week at the WG21 meeting), that the issue report be closed as Not A Defect. With your permission, I may turn this note into a short paper to document the reasoning underlying my position. Finally, regarding the perceived inconsistency in specification between the uniform_real- and uniform_int- distributions, it is the latter that is the odd man out. We specified Thanks again for drawing my attention to this report. Best, -- WEB |
Very interesting report on generate_canonical Seth! I've taken a look and it looks like libc++ is precisely following the specification. What is happening is that the standard specifies in this case that the result should be:
which theoretically should be just less than 1. However float does not have enough mantissa digits to accurately represent 4294967295. If you print both of these numbers out using std::hexfloat, which gives you the exact binary representation, you get: 0x1p+32 And subsequently the result becomes 1. I thought about doing everything in long double and then casting to RealType. However the standard explicitly says:
I'm open to suggestions from anyone listening on this. |
Re Walter's explanation: Also, this morning I realized another reason that fully open intervals are more appropriate for RealType: under "my" closed-interval proposal, what on earth would we return for |
Could that work at all? How do you distribute random numbers uniformly across an infinite range? |
Is there any progress on this? I recently (re-)discovered the problem, see http://stackoverflow.com/questions/25668600/is-1-0-a-valid-random-number |
Unaware of this thread, I raised issue #1 yet again here: My apolitical and implementation-agnostic opinions on this subject are as follows: On issue #1: a. It seems to me very natural to have [a,b) as the output range for b. I find it particularly undesirable to have
should not be returning NaNs depending on the Float type. Because of this issue, it is my opinion (as a user) that this is a bug with SL. On issue #2: c. IMO, the issue of what to do when |
*** Bug #34280 has been marked as a duplicate of this bug. *** |
Is there a solution? C++20 makes it "clear" that the [a, b) behavior is intended. |
Extended Description
The following program should complete successfully.
Using libc++ it instead produces an assertion failure. Other implementations (vc++ and libstdc++) exhibit the same behavior.
The text was updated successfully, but these errors were encountered: