Skip to content
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

Merged

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?

stl/inc/random Outdated Show resolved Hide resolved
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.
stl/inc/random Outdated Show resolved Hide resolved
stl/inc/random Outdated Show resolved Hide resolved
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.

stl/inc/random Outdated Show resolved Hide resolved
@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
stl/inc/random Outdated Show resolved Hide resolved
stl/inc/random Outdated Show resolved Hide resolved
stl/inc/random Outdated Show resolved Hide resolved
stl/inc/random Outdated Show resolved Hide resolved
stl/inc/random Outdated Show resolved Hide resolved
stl/inc/random Outdated Show resolved Hide resolved
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.

stl/inc/random Outdated Show resolved Hide resolved
stl/inc/random Outdated Show resolved Hide resolved
stl/inc/random Outdated Show resolved Hide resolved
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 StephanTLavavej removed their assignment Oct 2, 2020
@StephanTLavavej StephanTLavavej self-assigned this Oct 2, 2020
@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
4 participants