Fix the floating point version of midpoint. It wasn't constexpr, among other things. Add more tests. As a drive-by, the LCD implementation had a class named '__abs' which did a 'absolute value to a common-type' conversion. Rename that to be '__ct_abs'. git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@363714 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/numeric b/include/numeric index ba2fe26..2118704 100644 --- a/include/numeric +++ b/include/numeric
@@ -460,10 +460,10 @@ #if _LIBCPP_STD_VER > 14 -template <typename _Result, typename _Source, bool _IsSigned = is_signed<_Source>::value> struct __abs; +template <typename _Result, typename _Source, bool _IsSigned = is_signed<_Source>::value> struct __ct_abs; template <typename _Result, typename _Source> -struct __abs<_Result, _Source, true> { +struct __ct_abs<_Result, _Source, true> { _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY _Result operator()(_Source __t) const noexcept { @@ -474,7 +474,7 @@ }; template <typename _Result, typename _Source> -struct __abs<_Result, _Source, false> { +struct __ct_abs<_Result, _Source, false> { _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY _Result operator()(_Source __t) const noexcept { return __t; } }; @@ -500,8 +500,8 @@ using _Rp = common_type_t<_Tp,_Up>; using _Wp = make_unsigned_t<_Rp>; return static_cast<_Rp>(_VSTD::__gcd( - static_cast<_Wp>(__abs<_Rp, _Tp>()(__m)), - static_cast<_Wp>(__abs<_Rp, _Up>()(__n)))); + static_cast<_Wp>(__ct_abs<_Rp, _Tp>()(__m)), + static_cast<_Wp>(__ct_abs<_Rp, _Up>()(__n)))); } template<class _Tp, class _Up> @@ -516,8 +516,8 @@ return 0; using _Rp = common_type_t<_Tp,_Up>; - _Rp __val1 = __abs<_Rp, _Tp>()(__m) / _VSTD::gcd(__m, __n); - _Rp __val2 = __abs<_Rp, _Up>()(__n); + _Rp __val1 = __ct_abs<_Rp, _Tp>()(__m) / _VSTD::gcd(__m, __n); + _Rp __val2 = __ct_abs<_Rp, _Up>()(__n); _LIBCPP_ASSERT((numeric_limits<_Rp>::max() / __val1 > __val2), "Overflow in lcm"); return __val1 * __val2; } @@ -563,16 +563,23 @@ return (_Tp(0) < __val) - (__val < _Tp(0)); } +template <typename _Fp> +constexpr _Fp __fp_abs(_Fp __f) { return __f >= 0 ? __f : -__f; } + template <class _Fp> _LIBCPP_INLINE_VISIBILITY constexpr enable_if_t<is_floating_point_v<_Fp>, _Fp> midpoint(_Fp __a, _Fp __b) noexcept { - return isnormal(__a) && isnormal(__b) - && ((__sign(__a) != __sign(__b)) || ((numeric_limits<_Fp>::max() - abs(__a)) < abs(__b))) - ? __a / 2 + __b / 2 - : (__a + __b) / 2; + constexpr _Fp __lo = numeric_limits<_Fp>::min()*2; + constexpr _Fp __hi = numeric_limits<_Fp>::max()/2; + return __fp_abs(__a) <= __hi && __fp_abs(__b) <= __hi ? // typical case: overflow is impossible + (__a + __b)/2 : // always correctly rounded + __fp_abs(__a) < __lo ? __a + __b/2 : // not safe to halve a + __fp_abs(__a) < __lo ? __a/2 + __b : // not safe to halve b + __a/2 + __b/2; // otherwise correctly rounded } + #endif // _LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD
diff --git a/test/std/numerics/numeric.ops/numeric.ops.midpoint/midpoint.float.pass.cpp b/test/std/numerics/numeric.ops/numeric.ops.midpoint/midpoint.float.pass.cpp index 1a967f7..7d98348 100644 --- a/test/std/numerics/numeric.ops/numeric.ops.midpoint/midpoint.float.pass.cpp +++ b/test/std/numerics/numeric.ops/numeric.ops.midpoint/midpoint.float.pass.cpp
@@ -43,11 +43,11 @@ constexpr T minV = std::numeric_limits<T>::min(); // Things that can be compared exactly - assert((std::midpoint(T(0), T(0)) == T(0))); - assert((std::midpoint(T(2), T(4)) == T(3))); - assert((std::midpoint(T(4), T(2)) == T(3))); - assert((std::midpoint(T(3), T(4)) == T(3.5))); - assert((std::midpoint(T(0), T(0.4)) == T(0.2))); + static_assert((std::midpoint(T(0), T(0)) == T(0)), ""); + static_assert((std::midpoint(T(2), T(4)) == T(3)), ""); + static_assert((std::midpoint(T(4), T(2)) == T(3)), ""); + static_assert((std::midpoint(T(3), T(4)) == T(3.5)), ""); + static_assert((std::midpoint(T(0), T(0.4)) == T(0.2)), ""); // Things that can't be compared exactly constexpr T pct = fp_error_pct<T>(); @@ -70,6 +70,17 @@ assert((fptest_close_pct(std::midpoint(T(0), minV), minV/2, pct))); assert((fptest_close_pct(std::midpoint(maxV, maxV), maxV, pct))); assert((fptest_close_pct(std::midpoint(minV, minV), minV, pct))); + assert((fptest_close_pct(std::midpoint(maxV, minV), maxV/2, pct))); + assert((fptest_close_pct(std::midpoint(minV, maxV), maxV/2, pct))); + +// Near the min and the max + assert((fptest_close_pct(std::midpoint(maxV*T(0.75), maxV*T(0.50)), maxV*T(0.625), pct))); + assert((fptest_close_pct(std::midpoint(maxV*T(0.50), maxV*T(0.75)), maxV*T(0.625), pct))); + assert((fptest_close_pct(std::midpoint(minV*T(2), minV*T(8)), minV*T(5), pct))); + +// Big numbers of different signs + assert((fptest_close_pct(std::midpoint(maxV*T( 0.75), maxV*T(-0.5)), maxV*T( 0.125), pct))); + assert((fptest_close_pct(std::midpoint(maxV*T(-0.75), maxV*T( 0.5)), maxV*T(-0.125), pct))); // Denormalized values // TODO