The current implementation of generate_canonical yields NAN for float16_t and
fails to compile for float128_t on platforms where long double is IBM-128.
Indeed, it sets a long double variable based the provided RNG:
const long double __r = number_of_elements_in_the_range_of_the_RNG;
Later it multiplies __r to a variable __tmp of type _RealType, the provided
floating-point type:
__tmp *= __r;
When _RealType is float16_t the implicit cast from long double to float16_t
might yield infinity and subsequent calculations, notably the returned value,
become NAN.
When _RealType is float128_t (_Float128/__float128) and long double is IBM-128,
compilation fails because multiplication of IBM-128 and float128_t is undefined.
This patch fixes these issues by performing all intermediate calculations using
_CalcType which is the largest between _RealType and double. The end result is
cast to _RealType and returned.
_CalcType being at least double is probably enough to prevent __r from being
infinity. Nevertheless, this is static_asserted and, in case of failure, the
user is informed that the combination of _RealType and the provided RNG is
invalid.
PR libstdc++/121926
libstdc++-v3/ChangeLog:
* include/bits/random.h: Prevent narrowing warning in
uniform_real_distribution.
* include/bits/random.tcc: Fix generate_canonical.
*
testsuite/26_numerics/random/uniform_real_distribution/cons/stdfloat-c++23.cc:
New test.
---
libstdc++-v3/include/bits/random.h | 2 +-
libstdc++-v3/include/bits/random.tcc | 34 ++++++++---
.../cons/stdfloat-c++23.cc | 60 +++++++++++++++++++
3 files changed, 86 insertions(+), 10 deletions(-)
create mode 100644
libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/cons/stdfloat-c++23.cc
diff --git a/libstdc++-v3/include/bits/random.h
b/libstdc++-v3/include/bits/random.h
index 1fdaf51934f..b6ec07164ac 100644
--- a/libstdc++-v3/include/bits/random.h
+++ b/libstdc++-v3/include/bits/random.h
@@ -1930,7 +1930,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
*
* The lower bound is set to 0.0 and the upper bound to 1.0
*/
- uniform_real_distribution() : uniform_real_distribution(0.0) { }
+ uniform_real_distribution() : uniform_real_distribution(_RealType(0)) { }
/**
* @brief Constructs a uniform_real_distribution object.
diff --git a/libstdc++-v3/include/bits/random.tcc
b/libstdc++-v3/include/bits/random.tcc
index 53ccacb2e38..2c10007d7e8 100644
--- a/libstdc++-v3/include/bits/random.tcc
+++ b/libstdc++-v3/include/bits/random.tcc
@@ -3357,23 +3357,39 @@ namespace __detail
static_assert(std::is_floating_point<_RealType>::value,
"template argument must be a floating point type");
+ using _CalcType = typename std::conditional<
+ sizeof(_RealType) >= sizeof(double), _RealType, double>::type;
+
const size_t __b
- = std::min(static_cast<size_t>(std::numeric_limits<_RealType>::digits),
+ = std::min(static_cast<size_t>(std::numeric_limits<_CalcType>::digits),
__bits);
- const long double __r = static_cast<long double>(__urng.max())
- - static_cast<long double>(__urng.min()) + 1.0L;
- const size_t __log2r = std::log(__r) / std::log(2.0L);
+ constexpr _CalcType __r = static_cast<_CalcType>(__urng.max())
+ - static_cast<_CalcType>(__urng.min())
+ + _CalcType(1);
+
+#if _GLIBCXX_USE_C99_MATH_FUNCS
+ constexpr bool __is_finite = std::isfinite(__r);
+#else
+ constexpr bool __is_finite =
+ __r < std::numeric_limits<_CalcType>::infinity();
+#endif
+ static_assert(__r > _CalcType(0) && __is_finite,
+ "invalid combination of floating point type and
generator");
+
+ const size_t __log2r = std::log(__r) / std::log(_CalcType(2));
const size_t __m = std::max<size_t>(1UL,
(__b + __log2r - 1UL) / __log2r);
- _RealType __ret;
- _RealType __sum = _RealType(0);
- _RealType __tmp = _RealType(1);
+ _CalcType __res;
+ _CalcType __sum = _CalcType(0);
+ _CalcType __tmp = _CalcType(1);
for (size_t __k = __m; __k != 0; --__k)
{
- __sum += _RealType(__urng() - __urng.min()) * __tmp;
+ __sum += _CalcType(__urng() - __urng.min()) * __tmp;
__tmp *= __r;
}
- __ret = __sum / __tmp;
+ __res = __sum / __tmp;
+
+ _RealType __ret = static_cast<_RealType>(__res);
if (__builtin_expect(__ret >= _RealType(1), 0))
{
#if _GLIBCXX_USE_C99_MATH_FUNCS
diff --git
a/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/cons/stdfloat-c++23.cc
b/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/cons/stdfloat-c++23.cc
new file mode 100644
index 00000000000..ce78c17021b
--- /dev/null
+++
b/libstdc++-v3/testsuite/26_numerics/random/uniform_real_distribution/cons/stdfloat-c++23.cc
@@ -0,0 +1,60 @@
+// { dg-do run { target c++23 } }
+//
+// Copyright (C) 2008-2025 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <random>
+#include <stdfloat>
+#include <testsuite_hooks.h>
+
+template <typename Float>
+ void
+ test()
+ {
+ std::uniform_real_distribution<Float> u;
+ VERIFY( u.a() == Float{0} );
+ VERIFY( u.b() == Float{1} );
+ VERIFY( u.min() == Float{0} );
+ VERIFY( u.max() == Float{1} );
+
+ auto device = std::mt19937_64{};
+ for (unsigned n = 0; n < 10; ++n)
+ {
+ auto const x = u(device);
+ VERIFY( x >= Float{0} );
+ VERIFY( x < Float{1} );
+ }
+ }
+
+int main()
+{
+#ifdef __STDCPP_FLOAT16_T__
+ test<std::float16_t>();
+#endif
+#ifdef __STDCPP_FLOAT32_T__
+ test<std::float32_t >();
+#endif
+#ifdef __STDCPP_FLOAT64_T__
+ test<std::float64_t >();
+#endif
+#ifdef __STDCPP_FLOAT128_T__
+ test<std::float128_t>();
+#endif
+#ifdef __STDCPP_BFLOAT16_T__
+ test<std::bfloat16_t>();
+#endif
+}
--
2.51.0