https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110045
Bug ID: 110045 Summary: std::normal_distribution<float> (and likely others) give wrong min() and max() values Product: gcc Version: 12.2.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: gccbugs at elkpod dot com Target Milestone: --- >From my (non-expert) reading of the C++ spec (I'm using https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/n4950.pdf for the current 2023-05-10 draft), the min() and max() methods for std::normal_distribution<float> are returning incorrect values. "28.5.3.6 Random number distribution requirements" says that "A class D meets the requirements of a random number distribution if the expressions shown in Table 97 are valid and have the indicated semantics", and that in Table 97 "x" is a "(possibly const) value[s] of D", and that "x.min()" "Returns glb", and that "x.max()" "Returns lub", and that "glb and lub are values of T respectively corresponding to the greatest lower bound and the least upper bound on the values potentially returned by d's operator(), as determined by the current values of d's parameters". By my reading, this means that if I have "std::normal_distribution<float> norm {0, 1};", then "norm.max()" should return the least upper bound on the values potentially returned by norm's operator(). It does not seem to do that. For example, the highest value I was able to get norm() to generate was 0x1.ff13ccp+2 (== 7.985583). norm.max() reports 0x1.fffffep+127 (== 340282346638528859811704183484516925440.000000). While the values returned are technically valid bounds, they do not seem to be the least upper or greatest lower. I could find no Generator outputs which produced a value of 0x1.fffffep+127 from std::normal_distribution<float>. It is possible that my interpretation of the spec is wrong, and that somehow the min/max values should encompass all possible instantiations of std::normal_distribution, or some other loophole may exist. I could not find a better forum to find the answer to that; sorry. I have looked at the implementation of std::normal_distribution, and written the following code to generate what I believe to be the actual extreme values that are "potentially returned by norm's operator()". Reproduction code: #include <cstdio> #include <cstdint> #include <random> int32_t offsets[8] = { -1, +0, +0, -1, +0, +1, +1, +0 }; class SimpleGen { using result_type = uint32_t; public: result_type val, ctr = 0; static constexpr result_type min() { return 0; } static constexpr result_type max() { return 0xffffff; } result_type operator()() { val = 0x800000 + offsets[(ctr++)%8]; printf("\tG 0x%06x\n", val); return val; } }; int main(void) { SimpleGen gen; std::normal_distribution<float> norm {0, 1}; for (int i = 0; i < 8; i++) { float r = norm(gen); printf("%d %f %a\n", i, r, r); } printf("\n%f %a\n%f %a\n", norm.min(), norm.min(), norm.max(), norm.max()); } Build output: $ g++-12.2 -Wall -Wextra -o normdist2 normdist2.c -fno-strict-aliasing -fwrapv -fno-aggressive-loop-optimizations -fsanitize=undefined $ echo $? 0 Output: G 0x7fffff G 0x800000 0 0.000000 0x0p+0 1 -7.985583 -0x1.ff13ccp+2 G 0x800000 G 0x7fffff 2 -7.985583 -0x1.ff13ccp+2 3 0.000000 0x0p+0 G 0x800000 G 0x800001 4 7.985583 0x1.ff13ccp+2 5 0.000000 0x0p+0 G 0x800001 G 0x800000 6 0.000000 0x0p+0 7 7.985583 0x1.ff13ccp+2 -340282346638528859811704183484516925440.000000 -0x1.fffffep+127 340282346638528859811704183484516925440.000000 0x1.fffffep+127 Expected output: G 0x7fffff G 0x800000 0 0.000000 0x0p+0 1 -7.985583 -0x1.ff13ccp+2 G 0x800000 G 0x7fffff 2 -7.985583 -0x1.ff13ccp+2 3 0.000000 0x0p+0 G 0x800000 G 0x800001 4 7.985583 0x1.ff13ccp+2 5 0.000000 0x0p+0 G 0x800001 G 0x800000 6 0.000000 0x0p+0 7 7.985583 0x1.ff13ccp+2 -7.985583 -0x1.ff13ccp+2 7.985583 0x1.ff13ccp+2 $ g++-12.2 -v Using built-in specs. COLLECT_GCC=g++-12.2 COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-slackware-linux/12.2.0/lto-wrapper Target: x86_64-slackware-linux Configured with: ../gcc-12.2.0/configure --prefix=/usr/local --program-suffix=-12.2 -enable-languages=c,c++,lto --enable-lto --disable-multilib --with-gnu-ld --enable-threads --verbose --target=x86_64-slackware-linux --build=x86_64-slackware-linux --host=x86_64-slackware-linux --enable-tls --with-fpmath=avx --enable-__cxa_atexit --enable-gnu-indirect-function --enable-bootstrap --enable-libssp Thread model: posix Supported LTO compression algorithms: zlib gcc version 12.2.0 (GCC)