-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Fixes incorrect rounding in rejection method. #1159
Fixes incorrect rounding in rejection method. #1159
Conversation
- Should be based on floor(val) but implemented as trunction, so -1 < val < 0 is mistakenly accepted. The result 0 is therefore overrepresented.
Could you please add tests for the bug fix to the test suite? |
Most of the work is in a helper function that calculates the largest floating point number that truncates to less than or equal to a given unsigned integer.
5943374
to
1949495
Compare
1949495
to
d703774
Compare
stl/inc/random
Outdated
do { | ||
_Val = _CSTD log(_NRAND(_Eng, _Ty1)) / _Par0._Log_1_p; | ||
} while (_Val > _Ty1_max); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would we like to do for possible values of the distribution that are outside the range of the integral result type?
- Discard (reject): the probabilities of returning values within the range are all scaled by 1 / (1 - P(overflow)).
- Saturate to
numeric_limits<_Ty>::max()
, and perhaps raiseFE_INVALID
: the probabilities of returning values less thannumeric_limits<_Ty>::max()
are unchanged, the probability of returningnumeric_limits<_Ty>::max()
is increased by P(overflow).
(no changes requested, maybe decision needed from maintainers)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My preference is (1). Raising an FP exception gives the most flexibility, but also biases one of the results for anyone who doesn't trap the exception (the typical case, I expect). Effectively truncating and rescaling the idealized distribution seems like the least surprising thing to do, but I don't feel particularly strong about it. FWIW, it looks like libcxx clamps while libstdc++ rejects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My preference is also 1.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
- Need BSR operation for 64 bit types, even on x86. Borrowing _Bit_scan_reverse from charconv to xbit_ops.h to avoid duplication.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this looks great. Upon re-review I found extremely minor stylistic issues; I'll validate that the proposed fixes build and pass your tests, and I'll go ahead and push a commit to save time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI @cbezault, I pushed minor changes after you approved.
I need to push a fix for
#include <cassert>
#include <cstdio>
#include <limits>
#include <random>
using namespace std;
template <typename _Ty_32or64>
int TestMeow(_Ty_32or64 value) {
#ifdef _M_CEE_PURE
constexpr auto _Ty_32or64_digits = numeric_limits<_Ty_32or64>::digits;
return _Ty_32or64_digits - _Countl_zero_fallback(value);
#else // _M_CEE_PURE
return _Bit_scan_reverse(value);
#endif // _M_CEE_PURE
}
int main() {
assert(TestMeow(0x0000'0000u) == 0);
assert(TestMeow(0x0000'0001u) == 1);
assert(TestMeow(0x0000'0002u) == 2);
assert(TestMeow(0x0000'0004u) == 3);
assert(TestMeow(0x0000'0008u) == 4);
assert(TestMeow(0x1000'0000u) == 29);
assert(TestMeow(0x2000'0000u) == 30);
assert(TestMeow(0x4000'0000u) == 31);
assert(TestMeow(0x8000'0000u) == 32);
assert(TestMeow(0x0000'0000'0000'0000ull) == 0);
assert(TestMeow(0x0000'0000'0000'0001ull) == 1);
assert(TestMeow(0x0000'0000'0000'0002ull) == 2);
assert(TestMeow(0x0000'0000'0000'0004ull) == 3);
assert(TestMeow(0x0000'0000'0000'0008ull) == 4);
assert(TestMeow(0x1000'0000'0000'0000ull) == 61);
assert(TestMeow(0x2000'0000'0000'0000ull) == 62);
assert(TestMeow(0x4000'0000'0000'0000ull) == 63);
assert(TestMeow(0x8000'0000'0000'0000ull) == 64);
assert(TestMeow(0x0000'0003u) == 2);
assert(TestMeow(0x0000'0005u) == 3);
assert(TestMeow(0x0000'0009u) == 4);
assert(TestMeow(0x10F1'234Au) == 29);
assert(TestMeow(0x20F1'234Au) == 30);
assert(TestMeow(0x40F1'234Au) == 31);
assert(TestMeow(0x80F1'234Au) == 32);
assert(TestMeow(0x0000'0000'0000'0003ull) == 2);
assert(TestMeow(0x0000'0000'0000'0005ull) == 3);
assert(TestMeow(0x0000'0000'0000'0009ull) == 4);
assert(TestMeow(0x1000'000F'1234'A000ull) == 61);
assert(TestMeow(0x2000'000F'1234'A000ull) == 62);
assert(TestMeow(0x4000'000F'1234'A000ull) == 63);
assert(TestMeow(0x8000'000F'1234'A000ull) == 64);
puts("PASS");
}
|
Most intrinsics, including _BitScanReverse, are unavailable in /clr:pure mode. The most targeted way to fix this is to call _Countl_zero_fallback which is available from <limits>. I've manually tested that these codepaths behave identically.
Thanks again for fixing this silent bad codegen! We really appreciate it. 😺 |
Fixes #1001.
The "0 <= _Res" part of the rejection test should be based on floor(x). With truncation, -1 < x <0 is mistakenly accepted, causing 0 to be overrepresented in the output.
Fixes #1123.
Adds function to calculate largest float that will truncate down to a given unsigned value.