diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 1d3c7774fd9..44bf6e72c65 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,19 @@ +2019-03-05 Jonathan Wakely + + * include/c_compatibility/math.h [C++20] (lerp): Add using + declaration. + * include/c_global/cmath [C++20] (__cpp_lib_interpolate): Define. + (__lerp): Define function template to implement lerp. + (lerp(float, float, float), lerp(double, double, double)) + (lerp(long double, long double, long double)): Define for C++20. + * include/std/numeric [C++20] (__cpp_lib_interpolate): Define. + (midpoint(T, T), midpoint(T*, T*)): Define. + * include/std::version [C++20] (__cpp_lib_interpolate): Define. + * testsuite/26_numerics/lerp.cc: New test. + * testsuite/26_numerics/midpoint/floating.cc: New test. + * testsuite/26_numerics/midpoint/integral.cc: New test. + * testsuite/26_numerics/midpoint/pointer.cc: New test. + 2019-03-04 Edward Smith-Rowland <3dw4rd@verizon.net> PR libstdc++/88996 Implement P0439R0 diff --git a/libstdc++-v3/include/c_compatibility/math.h b/libstdc++-v3/include/c_compatibility/math.h index 4253f25fc91..d9fe94ca26e 100644 --- a/libstdc++-v3/include/c_compatibility/math.h +++ b/libstdc++-v3/include/c_compatibility/math.h @@ -177,5 +177,9 @@ using std::sph_neumannl; using std::sph_neumann; #endif // _GLIBCXX_USE_STD_SPEC_FUNCS +#if __cplusplus > 201703L +using std::lerp; +#endif // C++20 + #endif // _GLIBCXX_MATH_H #endif // __cplusplus diff --git a/libstdc++-v3/include/c_global/cmath b/libstdc++-v3/include/c_global/cmath index 121511cc8fb..b843c18f1da 100644 --- a/libstdc++-v3/include/c_global/cmath +++ b/libstdc++-v3/include/c_global/cmath @@ -1885,6 +1885,41 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #endif // C++17 +#if __cplusplus > 201703L + // linear interpolation +# define __cpp_lib_interpolate 201902L + + template + constexpr _Fp + __lerp(_Fp __a, _Fp __b, _Fp __t) + { + if (__a <= 0 && __b >= 0 || __a >= 0 && __b <= 0) + return __t * __b + (1 - __t) * __a; + + if (__t == 1) + return __b; // exact + + // Exact at __t=0, monotonic except near __t=1, + // bounded, determinate, and consistent: + const _Fp __x = __a + __t * (__b - __a); + return __t > 1 == __b > __a + ? (__b < __x ? __x : __b) + : (__b > __x ? __x : __b); // monotonic near __t=1 + } + + constexpr float + lerp(float __a, float __b, float __t) + { return std::__lerp(__a, __b, __t); } + + constexpr double + lerp(double __a, double __b, double __t) + { return std::__lerp(__a, __b, __t); } + + constexpr long double + lerp(long double __a, long double __b, long double __t) + { return std::__lerp(__a, __b, __t); } +#endif // C++20 + _GLIBCXX_END_NAMESPACE_VERSION } // namespace diff --git a/libstdc++-v3/include/std/numeric b/libstdc++-v3/include/std/numeric index c673e482812..ce02ee369f3 100644 --- a/libstdc++-v3/include/std/numeric +++ b/libstdc++-v3/include/std/numeric @@ -119,7 +119,7 @@ namespace __detail } } // namespace __detail -#if __cplusplus > 201402L +#if __cplusplus >= 201703L #define __cpp_lib_gcd_lcm 201606 // These were used in drafts of SD-6: @@ -156,6 +156,50 @@ namespace __detail #endif // C++17 +#if __cplusplus > 201703L + // midpoint +# define __cpp_lib_interpolate 201902L + +template + constexpr + enable_if_t<__and_, is_same, _Tp>, + __not_>>::value, + _Tp> + midpoint(_Tp __a, _Tp __b) noexcept + { + if constexpr (is_integral_v<_Tp>) + { + using _Up = make_unsigned_t<_Tp>; + + int __k = 1; + _Up __m = __a; + _Up __M = __b; + if (__a > __b) + { + __k = -1; + __m = __b; + __M = __a; + } + return __a + __k * _Tp(_Up(__M - __m) / 2); + } + else + { + return __builtin_isnormal(__a) && __builtin_isnormal(__b) + ? __a / 2 + __b / 2 + : (__a + __b) / 2; + } + } + + template + constexpr + enable_if_t<__and_, bool_constant>::value, + _Tp*> + midpoint(_Tp* __a, _Tp* __b) noexcept + { + return __a > __b ? __b + (__a - __b) / 2 : __a + (__b - __a) / 2; + } +#endif // C++20 + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index fa3d473759b..785e8966dbf 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -152,6 +152,7 @@ # define __cpp_lib_destroying_delete 201806L #endif #define __cpp_lib_erase_if 201900L +#define __cpp_lib_interpolate 201902L #ifdef _GLIBCXX_HAVE_BUILTIN_IS_CONSTANT_EVALUATED # define __cpp_lib_is_constant_evaluated 201811L #endif diff --git a/libstdc++-v3/testsuite/26_numerics/lerp.cc b/libstdc++-v3/testsuite/26_numerics/lerp.cc new file mode 100644 index 00000000000..fae74838554 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/lerp.cc @@ -0,0 +1,124 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include + +#ifndef __cpp_lib_interpolate +# error "Feature-test macro for midpoint and lerp missing" +#elif __cpp_lib_interpolate != 201902L +# error "Feature-test macro for midpoint and lerp has wrong value" +#endif + +#include +#include + +void +test01() +{ + using lim = std::numeric_limits; + + VERIFY( std::lerp(0.0, 1.0, 0.0) == 0.0 ); + VERIFY( std::lerp(-2.0, 10.0, 1.0) == 10.0 ); + VERIFY( std::lerp(2.0, -10.0, 1.0) == -10.0 ); + VERIFY( std::lerp(-8.0, 10.0, 0.5) == 1.0 ); + VERIFY( std::lerp(-10.0, 10.0, 0.25) == -5.0 ); + VERIFY( std::lerp(10.0, -10.0, 0.375) == 2.5 ); + + VERIFY( std::lerp(2.0, 2.0, 200.0) == 2.0 ); + VERIFY( std::lerp(2.0, 4.0, 200.0) == 402.0 ); + VERIFY( std::lerp(2.0, 4.0, -20.0) == -38.0 ); + + VERIFY( std::lerp(1.1, 30201.1, 0) == 1.1 ); + VERIFY( std::lerp(1.1, 30201.1, 1) == 30201.1 ); + VERIFY( std::lerp(1.1, -30201.1, 0) == 1.1 ); + VERIFY( std::lerp(1.1, -30201.1, 1) == -30201.1 ); + + VERIFY( std::lerp(1.1, 1.1, lim::infinity()) == 1.1 ); + VERIFY( std::isfinite(std::lerp(1.1, 1.1+lim::min(), lim::max())) ); + + VERIFY( std::lerp(lim::max(), lim::max(), 1) == lim::max() ); + VERIFY( std::lerp(lim::max(), lim::max()/9e9, 0) == lim::max() ); + VERIFY( std::lerp(lim::max()/9e9, lim::max(), 1) == lim::max() ); +} + +void +test02() +{ + using lim = std::numeric_limits; + + VERIFY( std::lerp(0.0f, 1.0f, 0.0f) == 0.0f ); + VERIFY( std::lerp(-2.0f, 10.0f, 1.0f) == 10.0f ); + VERIFY( std::lerp(2.0f, -10.0f, 1.0f) == -10.0f ); + VERIFY( std::lerp(-8.0f, 10.0f, 0.5f) == 1.0f ); + VERIFY( std::lerp(-10.0f, 10.0f, 0.25f) == -5.0f ); + VERIFY( std::lerp(10.0f, -10.0f, 0.375f) == 2.5f ); + + VERIFY( std::lerp(2.0f, 2.0f, 200.0f) == 2.0f ); + VERIFY( std::lerp(2.0f, 4.0f, 200.0f) == 402.0f ); + VERIFY( std::lerp(2.0f, 4.0f, -20.0f) == -38.0f ); + + VERIFY( std::lerp(1.1f, 30201.1f, 0) == 1.1f ); + VERIFY( std::lerp(1.1f, 30201.1f, 1) == 30201.1f ); + VERIFY( std::lerp(1.1f, -30201.1f, 0) == 1.1f ); + VERIFY( std::lerp(1.1f, -30201.1f, 1) == -30201.1f ); + + VERIFY( std::lerp(1.1f, 1.1f, lim::infinity()) == 1.1f ); + VERIFY( std::isfinite(std::lerp(1.1f, 1.1f+lim::min(), lim::max())) ); + + VERIFY( std::lerp(lim::max(), lim::max(), 1) == lim::max() ); + VERIFY( std::lerp(lim::max(), lim::max()/9e9f, 0) == lim::max() ); + VERIFY( std::lerp(lim::max()/9e9f, lim::max(), 1) == lim::max() ); +} + +void +test03() +{ + using lim = std::numeric_limits; + + VERIFY( std::lerp(0.0l, 1.0l, 0.0l) == 0.0l ); + VERIFY( std::lerp(-2.0l, 10.0l, 1.0l) == 10.0l ); + VERIFY( std::lerp(2.0l, -10.0l, 1.0l) == -10.0l ); + VERIFY( std::lerp(-8.0l, 10.0l, 0.5l) == 1.0l ); + VERIFY( std::lerp(-10.0l, 10.0l, 0.25l) == -5.0l ); + VERIFY( std::lerp(10.0l, -10.0l, 0.375l) == 2.5l ); + + VERIFY( std::lerp(2.0l, 2.0l, 200.0l) == 2.0l ); + VERIFY( std::lerp(2.0l, 4.0l, 200.0l) == 402.0l ); + VERIFY( std::lerp(2.0l, 4.0l, -20.0l) == -38.0l ); + + VERIFY( std::lerp(1.1l, 30201.1l, 0) == 1.1l ); + VERIFY( std::lerp(1.1l, 30201.1l, 1) == 30201.1l ); + VERIFY( std::lerp(1.1l, -30201.1l, 0) == 1.1l ); + VERIFY( std::lerp(1.1l, -30201.1l, 1) == -30201.1l ); + + VERIFY( std::lerp(1.1l, 1.1l, lim::infinity()) == 1.1l ); + VERIFY( std::isfinite(std::lerp(1.1l, 1.1l+lim::min(), lim::max())) ); + + VERIFY( std::lerp(lim::max(), lim::max(), 1) == lim::max() ); + VERIFY( std::lerp(lim::max(), lim::max()/9e9l, 0) == lim::max() ); + VERIFY( std::lerp(lim::max()/9e9l, lim::max(), 1) == lim::max() ); +} + +int main() +{ + test01(); + test02(); + test03(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/midpoint/floating.cc b/libstdc++-v3/testsuite/26_numerics/midpoint/floating.cc new file mode 100644 index 00000000000..9c6e4113b18 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/midpoint/floating.cc @@ -0,0 +1,65 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include +#include +#include + +void +test01() +{ + using lim = std::numeric_limits; + + VERIFY( std::midpoint(2.0, 4.0) == 3.0 ); + VERIFY( std::midpoint(0.0, 0.4) == 0.2 ); + VERIFY( std::midpoint(0.0, -0.0) == 0.0 ); + VERIFY( std::midpoint(9e9, -9e9) == 0.0 ); + + VERIFY( std::midpoint(lim::max(), lim::max()) == lim::max() ); +} + +void +test02() +{ + using lim = std::numeric_limits; + + VERIFY( std::midpoint(2.0f, 4.0f) == 3.0f ); + VERIFY( std::midpoint(0.0f, 0.4f) == 0.2f ); + VERIFY( std::midpoint(0.0f, -0.0f) == 0.0f ); + VERIFY( std::midpoint(9e9f, -9e9f) == 0.0f ); +} + +void +test03() +{ + using lim = std::numeric_limits; + + VERIFY( std::midpoint(2.0l, 4.0l) == 3.0l ); + VERIFY( std::midpoint(0.0l, 0.4l) == 0.2l ); + VERIFY( std::midpoint(0.0l, -0.0l) == 0.0l ); + VERIFY( std::midpoint(9e9l, -9e9l) == 0.0l ); +} + +int main() +{ + test01(); + test02(); + test03(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/midpoint/integral.cc b/libstdc++-v3/testsuite/26_numerics/midpoint/integral.cc new file mode 100644 index 00000000000..4fe66df3062 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/midpoint/integral.cc @@ -0,0 +1,121 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include + +#ifndef __cpp_lib_interpolate +# error "Feature-test macro for midpoint and lerp missing" +#elif __cpp_lib_interpolate != 201902L +# error "Feature-test macro for midpoint and lerp has wrong value" +#endif + +#include +#include + +static_assert(std::is_same_v); +static_assert(noexcept(std::midpoint(1, 2))); + +struct test_type { }; +template decltype(std::midpoint(T(), T())) try_midpoint(int); +template test_type try_midpoint(...); +template constexpr bool no_midpoint() +{ return std::is_same_v()), test_type>; } + +static_assert(no_midpoint()); +static_assert(no_midpoint()); +static_assert(no_midpoint()); +static_assert(no_midpoint()); + +static_assert( std::midpoint(0, 0) == 0 ); +static_assert( std::midpoint(1, 1) == 1 ); +static_assert( std::midpoint(0, 1) == 0 ); +static_assert( std::midpoint(1, 0) == 1 ); +static_assert( std::midpoint(0, 2) == 1 ); +static_assert( std::midpoint(3, 2) == 3 ); +static_assert( std::midpoint(-5, 4) == -1 ); +static_assert( std::midpoint(5, -4) == 1 ); +static_assert( std::midpoint(-5, -4) == -5 ); +static_assert( std::midpoint(-4, -5) == -4 ); +static_assert( std::midpoint(INT_MIN, INT_MAX) == -1 ); +static_assert( std::midpoint(INT_MAX, INT_MIN) == 0 ); +static_assert( std::midpoint(INT_MAX, INT_MAX) == INT_MAX ); +static_assert( std::midpoint(INT_MAX, INT_MAX-1) == INT_MAX ); +static_assert( std::midpoint(INT_MAX-1, INT_MAX-1) == INT_MAX-1 ); +static_assert( std::midpoint(INT_MAX-1, INT_MAX) == INT_MAX-1 ); +static_assert( std::midpoint(INT_MAX, INT_MAX-2) == INT_MAX-1 ); + +static_assert( std::midpoint(0u, 0u) == 0 ); +static_assert( std::midpoint(0u, 1u) == 0 ); +static_assert( std::midpoint(1u, 0u) == 1 ); +static_assert( std::midpoint(0u, 2u) == 1 ); +static_assert( std::midpoint(3u, 2u) == 3 ); +static_assert( std::midpoint(0u, UINT_MAX) == UINT_MAX/2 ); +static_assert( std::midpoint(UINT_MAX, 0u) == (UINT_MAX/2 + 1) ); +static_assert( std::midpoint(UINT_MAX, UINT_MAX) == UINT_MAX ); +static_assert( std::midpoint(UINT_MAX, UINT_MAX-1) == UINT_MAX ); +static_assert( std::midpoint(UINT_MAX-1, UINT_MAX-1) == UINT_MAX-1 ); +static_assert( std::midpoint(UINT_MAX-1, UINT_MAX) == UINT_MAX-1 ); +static_assert( std::midpoint(UINT_MAX, UINT_MAX-2) == UINT_MAX-1 ); + +static_assert( std::midpoint(0, 0) == 0 ); +static_assert( std::midpoint(0, 1) == 0 ); +static_assert( std::midpoint(1, 0) == 1 ); +static_assert( std::midpoint(0, 2) == 1 ); +static_assert( std::midpoint(3, 2) == 3 ); +static_assert( std::midpoint(-5, 4) == -1 ); +static_assert( std::midpoint(5, -4) == 1 ); +static_assert( std::midpoint(-5, -4) == -5 ); +static_assert( std::midpoint(-4, -5) == -4 ); +static_assert( std::midpoint(SHRT_MIN, SHRT_MAX) == -1 ); +static_assert( std::midpoint(SHRT_MAX, SHRT_MIN) == 0 ); +static_assert( std::midpoint(SHRT_MAX, SHRT_MAX) == SHRT_MAX ); +static_assert( std::midpoint(SHRT_MAX, SHRT_MAX-1) == SHRT_MAX ); +static_assert( std::midpoint(SHRT_MAX-1, SHRT_MAX-1) == SHRT_MAX-1 ); +static_assert( std::midpoint(SHRT_MAX-1, SHRT_MAX) == SHRT_MAX-1 ); +static_assert( std::midpoint(SHRT_MAX, SHRT_MAX-2) == SHRT_MAX-1 ); + +static_assert( std::midpoint(0, 0) == 0 ); +static_assert( std::midpoint(1, 1) == 1 ); +static_assert( std::midpoint(0, 1) == 0 ); +static_assert( std::midpoint(1, 0) == 1 ); +static_assert( std::midpoint(0, 2) == 1 ); +static_assert( std::midpoint(3, 2) == 3 ); +static_assert( std::midpoint(-5, 4) == -1 ); +static_assert( std::midpoint(5, -4) == 1 ); +static_assert( std::midpoint(-5, -4) == -5 ); +static_assert( std::midpoint(-4, -5) == -4 ); +static_assert( std::midpoint(SCHAR_MIN, SCHAR_MAX) == -1 ); +static_assert( std::midpoint(SCHAR_MAX, SCHAR_MIN) == 0 ); +static_assert( std::midpoint(SCHAR_MAX, SCHAR_MAX) == SCHAR_MAX ); +static_assert( std::midpoint(SCHAR_MAX, SCHAR_MAX-1) == SCHAR_MAX); + +void +test01() +{ + // Test every possibility for signed char. + for (int a = SCHAR_MIN; a <= SCHAR_MAX; ++a) + for (int b = SCHAR_MIN; b <= SCHAR_MAX; ++b) + VERIFY( std::midpoint(a, b) == std::midpoint(a, b) ); +} + +int main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/midpoint/pointer.cc b/libstdc++-v3/testsuite/26_numerics/midpoint/pointer.cc new file mode 100644 index 00000000000..bd586d28ba0 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/midpoint/pointer.cc @@ -0,0 +1,62 @@ +// Copyright (C) 2019 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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include +#include +#include + +const int* p = nullptr; +static_assert(std::is_same_v); +// This is a GNU extension: +static_assert(noexcept(std::midpoint(p, p))); + +struct test_type { }; +template decltype(std::midpoint((T*)0, (T*)0)) try_midpoint(int); +template test_type try_midpoint(...); +template constexpr bool no_midpoint() +{ return std::is_same_v()), test_type>; } + +static_assert(no_midpoint()); +static_assert(no_midpoint()); +static_assert(no_midpoint()); +static_assert(no_midpoint()); + +constexpr int ca[3] = {}; +static_assert( std::midpoint(ca, ca+3) == ca+1 ); + +void +test01() +{ + int a[4]; + VERIFY( std::midpoint(a, a) == a ); + VERIFY( std::midpoint(a, a+1) == a ); + VERIFY( std::midpoint(a, a+2) == a+1 ); + VERIFY( std::midpoint(a, a+3) == a+1 ); + VERIFY( std::midpoint(a, a+4) == a+2 ); + VERIFY( std::midpoint(a+1, a) == a+1 ); + VERIFY( std::midpoint(a+2, a) == a+1 ); + VERIFY( std::midpoint(a+3, a) == a+2 ); + VERIFY( std::midpoint(a+4, a) == a+2 ); +} + +int main() +{ + test01(); +}