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

Reply via email to