[libcxx-commits] [libcxx] r370891 - [libc++] Add `__truncating_cast` for safely casting float types to integers

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed Sep 4 05:48:32 PDT 2019


Author: ldionne
Date: Wed Sep  4 05:48:32 2019
New Revision: 370891

URL: http://llvm.org/viewvc/llvm-project?rev=370891&view=rev
Log:
[libc++] Add `__truncating_cast` for safely casting float types to integers

This is needed anytime we need to clamp an arbitrary floating point
value to an integer type.

Thanks to Eric Fiselier for the patch.

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

Added:
    libcxx/trunk/test/libcxx/numerics/clamp_to_integral.pass.cpp
Modified:
    libcxx/trunk/include/math.h

Modified: libcxx/trunk/include/math.h
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/math.h?rev=370891&r1=370890&r2=370891&view=diff
==============================================================================
--- libcxx/trunk/include/math.h (original)
+++ libcxx/trunk/include/math.h Wed Sep  4 05:48:32 2019
@@ -1553,6 +1553,40 @@ inline _LIBCPP_INLINE_VISIBILITY
 typename std::enable_if<std::is_integral<_A1>::value, double>::type
 trunc(_A1 __lcpp_x) _NOEXCEPT {return ::trunc((double)__lcpp_x);}
 
+_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 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 = std::numeric_limits<_IntT>;
+  const _IntT _MaxVal = std::__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
+
 } // extern "C++"
 
 #endif // __cplusplus

Added: libcxx/trunk/test/libcxx/numerics/clamp_to_integral.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/libcxx/numerics/clamp_to_integral.pass.cpp?rev=370891&view=auto
==============================================================================
--- libcxx/trunk/test/libcxx/numerics/clamp_to_integral.pass.cpp (added)
+++ libcxx/trunk/test/libcxx/numerics/clamp_to_integral.pass.cpp Wed Sep  4 05:48:32 2019
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// __clamp_to_integral<IntT>(RealT)
+
+// Test the conversion function that truncates floating point types to the
+// 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>
+
+template <class IntT>
+void test() {
+  typedef std::numeric_limits<IntT> Lim;
+  const bool MaxIsRepresentable = sizeof(IntT) < 8;
+  const bool IsSigned = std::is_signed<IntT>::value;
+  struct TestCase {
+    double Input;
+    IntT Expect;
+    bool IsRepresentable;
+  } TestCases[] = {
+      {0, 0, true},
+      {1, 1, true},
+      {IsSigned ? static_cast<IntT>(-1) : 0,
+       IsSigned ? static_cast<IntT>(-1) : 0, true},
+      {Lim::lowest(), Lim::lowest(), true},
+      {static_cast<double>(Lim::max()), Lim::max(), MaxIsRepresentable},
+      {static_cast<double>(Lim::max()) + 1, Lim::max(), false},
+      {static_cast<double>(Lim::max()) + 1024, Lim::max(), false},
+      {nextafter(static_cast<double>(Lim::max()), INFINITY), Lim::max(), false},
+  };
+  for (TestCase TC : TestCases) {
+    auto res = std::__clamp_to_integral<IntT>(TC.Input);
+    assert(res == TC.Expect);
+    if (TC.IsRepresentable) {
+      auto other = static_cast<IntT>(std::trunc(TC.Input));
+      assert(res == other);
+    } else
+      assert(res == Lim::min() || res == Lim::max());
+  }
+}
+
+template <class IntT>
+void test_float() {
+  typedef std::numeric_limits<IntT> Lim;
+  const bool MaxIsRepresentable = sizeof(IntT) < 4;
+  ((void)MaxIsRepresentable);
+  const bool IsSigned = std::is_signed<IntT>::value;
+  struct TestCase {
+    float Input;
+    IntT Expect;
+    bool IsRepresentable;
+  } TestCases[] = {
+      {0, 0, true},
+      {1, 1, true},
+      {IsSigned ? static_cast<IntT>(-1) : 0,
+       IsSigned ? static_cast<IntT>(-1) : 0, true},
+      {Lim::lowest(), Lim::lowest(), true},
+      {static_cast<float>(Lim::max()), Lim::max(), MaxIsRepresentable },
+       {nextafter(static_cast<float>(Lim::max()), INFINITY), Lim::max(), false},
+  };
+  for (TestCase TC : TestCases) {
+    auto res = std::__clamp_to_integral<IntT>(TC.Input);
+    assert(res == TC.Expect);
+    if (TC.IsRepresentable) {
+      auto other = static_cast<IntT>(std::trunc(TC.Input));
+      assert(res == other);
+    } else
+      assert(res == Lim::min() || res == Lim::max());
+  }
+}
+
+int main() {
+  test<short>();
+  test<unsigned short>();
+  test<int>();
+  test<unsigned>();
+  test<long long>();
+  test<unsigned long long>();
+  test_float<short>();
+  test_float<int>();
+  test_float<long long>();
+}




More information about the libcxx-commits mailing list