Skip to content

Conversation

@MattStephanson
Copy link
Contributor

@MattStephanson MattStephanson commented Aug 7, 2020

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.

 - Should be based on floor(val) but implemented as trunction, so
   -1 < val < 0 is mistakenly accepted. The result 0 is therefore
   overrepresented.
@MattStephanson MattStephanson requested a review from a team as a code owner August 7, 2020 04:53
@StephanTLavavej StephanTLavavej added the bug Something isn't working label Aug 8, 2020
@statementreply
Copy link
Contributor

Could you please add tests for the bug fix to the test suite?

MattStephanson and others added 3 commits August 16, 2020 23:06
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.
@MattStephanson MattStephanson force-pushed the random_rejection_rounding branch from 5943374 to 1949495 Compare August 17, 2020 07:15
@MattStephanson MattStephanson force-pushed the random_rejection_rounding branch from 1949495 to d703774 Compare August 17, 2020 07:24
stl/inc/random Outdated
Comment on lines 2169 to 2171
do {
_Val = _CSTD log(_NRAND(_Eng, _Ty1)) / _Par0._Log_1_p;
} while (_Val > _Ty1_max);
Copy link
Contributor

@statementreply statementreply Aug 17, 2020

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?

  1. Discard (reject): the probabilities of returning values within the range are all scaled by 1 / (1 - P(overflow)).
  2. Saturate to numeric_limits<_Ty>::max(), and perhaps raise FE_INVALID: the probabilities of returning values less than numeric_limits<_Ty>::max() are unchanged, the probability of returning numeric_limits<_Ty>::max() is increased by P(overflow).

(no changes requested, maybe decision needed from maintainers)

Copy link
Contributor Author

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.

https://github.com/llvm/llvm-project/blob/6a64079699e7b56badd292e39cad4b8bfe941aec/libcxx/include/random#L4719

https://github.com/gcc-mirror/gcc/blob/3eeede6de7f6021ad726f034401872f6d58b343d/libstdc%2B%2B-v3/include/bits/random.tcc#L1350

Copy link
Contributor

@cbezault cbezault Sep 2, 2020

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.

@StephanTLavavej

This comment has been minimized.

@MattStephanson

This comment has been minimized.

MattStephanson and others added 3 commits August 18, 2020 16:42
 - Need BSR operation for 64 bit types, even on x86. Borrowing
   _Bit_scan_reverse from charconv to xbit_ops.h to avoid
   duplication.
@MattStephanson MattStephanson marked this pull request as draft August 19, 2020 00:18
@StephanTLavavej

This comment has been minimized.

@MattStephanson MattStephanson marked this pull request as ready for review August 21, 2020 01:26
Copy link
Member

@StephanTLavavej StephanTLavavej left a 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.

Copy link
Member

@StephanTLavavej StephanTLavavej left a 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.

@StephanTLavavej
Copy link
Member

I need to push a fix for /clr:pure. Manual targeted testing:

C:\Temp>type meow.cpp
#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");
}
C:\Temp>cl /EHsc /nologo /W4 meow.cpp && meow
meow.cpp
PASS

C:\Temp>cl /clr /nologo /W4 meow.cpp && meow
meow.cpp
PASS

C:\Temp>cl /clr:pure /nologo /W4 meow.cpp && meow
cl : Command line warning D9035 : option 'clr:pure' has been deprecated and will be removed in a future release
meow.cpp
S:\msvc\binaries\x86chk\inc\yvals.h(245): warning STL4001: /clr:pure is deprecated and will be REMOVED.
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.
@StephanTLavavej StephanTLavavej merged commit c385d02 into microsoft:master Oct 3, 2020
@StephanTLavavej
Copy link
Member

Thanks again for fixing this silent bad codegen! We really appreciate it. 😺

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

<random>: UB in random number distributions: type casting out of range <random>: binomial_distribution is broken

4 participants