[libc++] Extract __clamp_to_integral to its own header

In addition to being more consistent with our approach for helpers, this
solves an actual issue where <cmath> was using numeric_limits but never
including the <limits> header directly. In a normal setup, this is not
an issue because the <math.h> header included by <cmath> does include
<limits>. However, I did stumble upon some code where that didn't work,
most likely because they were placing their own <math.h> header in front
of ours. I didn't bother investigating further.

Differential Revision: https://reviews.llvm.org/D115282

NOKEYCHECK=True
GitOrigin-RevId: 81eda008e952e6b46d20b97f7fbfd6f2e69bd3a1
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index df91edb..17f760c 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -257,6 +257,7 @@
   __random/binomial_distribution.h
   __random/cauchy_distribution.h
   __random/chi_squared_distribution.h
+  __random/clamp_to_integral.h
   __random/default_random_engine.h
   __random/discard_block_engine.h
   __random/discrete_distribution.h
diff --git a/include/__random/clamp_to_integral.h b/include/__random/clamp_to_integral.h
new file mode 100644
index 0000000..dd5d2b0
--- /dev/null
+++ b/include/__random/clamp_to_integral.h
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___RANDOM_CLAMP_TO_INTEGRAL_H
+#define _LIBCPP___RANDOM_CLAMP_TO_INTEGRAL_H
+
+#include <__config>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _IntT, class _FloatT,
+    bool _FloatBigger = (numeric_limits<_FloatT>::digits > numeric_limits<_IntT>::digits),
+    int _Bits = (numeric_limits<_IntT>::digits - numeric_limits<_FloatT>::digits)>
+_LIBCPP_INLINE_VISIBILITY
+_LIBCPP_CONSTEXPR _IntT __max_representable_int_for_float() _NOEXCEPT {
+  static_assert(is_floating_point<_FloatT>::value, "must be a floating point type");
+  static_assert(is_integral<_IntT>::value, "must be an integral type");
+  static_assert(numeric_limits<_FloatT>::radix == 2, "FloatT has incorrect radix");
+  static_assert((_IsSame<_FloatT, float>::value || _IsSame<_FloatT, double>::value
+                 || _IsSame<_FloatT,long double>::value), "unsupported floating point type");
+  return _FloatBigger ? numeric_limits<_IntT>::max() :  (numeric_limits<_IntT>::max() >> _Bits << _Bits);
+}
+
+// Convert a floating point number to the specified integral type after
+// clamping to the integral type's representable range.
+//
+// The behavior is undefined if `__r` is NaN.
+template <class _IntT, class _RealT>
+_LIBCPP_INLINE_VISIBILITY
+_IntT __clamp_to_integral(_RealT __r) _NOEXCEPT {
+  using _Lim = numeric_limits<_IntT>;
+  const _IntT _MaxVal = __max_representable_int_for_float<_IntT, _RealT>();
+  if (__r >= ::nextafter(static_cast<_RealT>(_MaxVal), INFINITY)) {
+    return _Lim::max();
+  } else if (__r <= _Lim::lowest()) {
+    return _Lim::min();
+  }
+  return static_cast<_IntT>(__r);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANDOM_CLAMP_TO_INTEGRAL_H
diff --git a/include/__random/poisson_distribution.h b/include/__random/poisson_distribution.h
index fb213b0..d157e8f 100644
--- a/include/__random/poisson_distribution.h
+++ b/include/__random/poisson_distribution.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___RANDOM_POISSON_DISTRIBUTION_H
 
 #include <__config>
+#include <__random/clamp_to_integral.h>
 #include <__random/exponential_distribution.h>
 #include <__random/normal_distribution.h>
 #include <__random/uniform_real_distribution.h>
diff --git a/include/cmath b/include/cmath
index 3a7985f..b5c332c 100644
--- a/include/cmath
+++ b/include/cmath
@@ -638,36 +638,6 @@
 
 #endif // _LIBCPP_STD_VER > 17
 
-template <class _IntT, class _FloatT,
-    bool _FloatBigger = (numeric_limits<_FloatT>::digits > numeric_limits<_IntT>::digits),
-    int _Bits = (numeric_limits<_IntT>::digits - numeric_limits<_FloatT>::digits)>
-_LIBCPP_INLINE_VISIBILITY
-_LIBCPP_CONSTEXPR _IntT __max_representable_int_for_float() _NOEXCEPT {
-  static_assert(is_floating_point<_FloatT>::value, "must be a floating point type");
-  static_assert(is_integral<_IntT>::value, "must be an integral type");
-  static_assert(numeric_limits<_FloatT>::radix == 2, "FloatT has incorrect radix");
-  static_assert((_IsSame<_FloatT, float>::value || _IsSame<_FloatT, double>::value
-                 || _IsSame<_FloatT,long double>::value), "unsupported floating point type");
-  return _FloatBigger ? numeric_limits<_IntT>::max() :  (numeric_limits<_IntT>::max() >> _Bits << _Bits);
-}
-
-// Convert a floating point number to the specified integral type after
-// clamping to the integral types representable range.
-//
-// The behavior is undefined if `__r` is NaN.
-template <class _IntT, class _RealT>
-_LIBCPP_INLINE_VISIBILITY
-_IntT __clamp_to_integral(_RealT __r) _NOEXCEPT {
-  using _Lim = numeric_limits<_IntT>;
-  const _IntT _MaxVal = __max_representable_int_for_float<_IntT, _RealT>();
-  if (__r >= ::nextafter(static_cast<_RealT>(_MaxVal), INFINITY)) {
-    return _Lim::max();
-  } else if (__r <= _Lim::lowest()) {
-    return _Lim::min();
-  }
-  return static_cast<_IntT>(__r);
-}
-
 _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
diff --git a/include/module.modulemap b/include/module.modulemap
index ea0a6f8..ae1d2ae 100644
--- a/include/module.modulemap
+++ b/include/module.modulemap
@@ -704,6 +704,7 @@
       module binomial_distribution           { private header "__random/binomial_distribution.h" }
       module cauchy_distribution             { private header "__random/cauchy_distribution.h" }
       module chi_squared_distribution        { private header "__random/chi_squared_distribution.h" }
+      module clamp_to_integral               { private header "__random/clamp_to_integral.h" }
       module default_random_engine           { private header "__random/default_random_engine.h" }
       module discard_block_engine            { private header "__random/discard_block_engine.h" }
       module discrete_distribution           { private header "__random/discrete_distribution.h" }
diff --git a/include/random b/include/random
index 9eb70ba..c88bfce 100644
--- a/include/random
+++ b/include/random
@@ -1682,6 +1682,7 @@
 #include <__random/binomial_distribution.h>
 #include <__random/cauchy_distribution.h>
 #include <__random/chi_squared_distribution.h>
+#include <__random/clamp_to_integral.h>
 #include <__random/default_random_engine.h>
 #include <__random/discard_block_engine.h>
 #include <__random/discrete_distribution.h>
diff --git a/test/libcxx/diagnostics/detail.headers/random/clamp_to_integral.module.verify.cpp b/test/libcxx/diagnostics/detail.headers/random/clamp_to_integral.module.verify.cpp
new file mode 100644
index 0000000..9909f48
--- /dev/null
+++ b/test/libcxx/diagnostics/detail.headers/random/clamp_to_integral.module.verify.cpp
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: modules-build
+
+// WARNING: This test was generated by 'generate_private_header_tests.py'
+// and should not be edited manually.
+
+// expected-error@*:* {{use of private header from outside its module: '__random/clamp_to_integral.h'}}
+#include <__random/clamp_to_integral.h>
diff --git a/test/libcxx/numerics/clamp_to_integral.pass.cpp b/test/libcxx/numerics/clamp_to_integral.pass.cpp
index cbaf4b7..a826555 100644
--- a/test/libcxx/numerics/clamp_to_integral.pass.cpp
+++ b/test/libcxx/numerics/clamp_to_integral.pass.cpp
@@ -12,9 +12,10 @@
 // closest representable value for the specified integer type, or
 // numeric_limits<IntT>::max()/min() if the value isn't representable.
 
-#include <limits>
 #include <cassert>
 #include <cmath>
+#include <limits>
+#include <random> // for __clamp_to_integral
 
 template <class IntT>
 void test() {