From d8dbaffcbaa05dda8feda125a855ed10fc1ae101 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 4 May 2020 20:02:17 -0700 Subject: [PATCH 1/3] Fix complex's Sufficient Additional Overloads * GH-785 reported that `arg(real)` was implemented as `return 0;` but `arg(complex)` does actual work for complex numbers with zero imaginary parts. * `norm` should upgrade to `double` before squaring. The difference is observable because `double`'s range is vast. * `conj` and `proj` should return `complex`. * `conj(real)` should behave exactly like `conj(complex)`, returning an imaginary part of negative zero. The difference is technically observable. * `proj(real)` should behave exactly like `proj(complex)`, mapping negative infinity to positive infinity. --- stl/inc/complex | 37 +++++++++++++++---- .../test.cpp | 37 +++++++++++++++++++ 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/stl/inc/complex b/stl/inc/complex index dc9ced16f3b..97c3ddd4c67 100644 --- a/stl/inc/complex +++ b/stl/inc/complex @@ -1758,8 +1758,11 @@ template using _Upgrade_to_double = conditional_t, double, _Ty>; template , int> = 0> -_NODISCARD _Upgrade_to_double<_Ty> arg(_Ty) { - return 0; +_NODISCARD _Upgrade_to_double<_Ty> arg(_Ty _Left) { + using _Upgraded = _Upgrade_to_double<_Ty>; + const auto _Val = static_cast<_Upgraded>(_Left); + + return _Ctraits<_Upgraded>::atan2(0, _Val); } template , int> = 0> @@ -1769,22 +1772,40 @@ _NODISCARD _CONSTEXPR20 _Upgrade_to_double<_Ty> imag(_Ty) { template , int> = 0> _NODISCARD _CONSTEXPR20 _Upgrade_to_double<_Ty> real(_Ty _Left) { - return static_cast<_Upgrade_to_double<_Ty>>(_Left); + using _Upgraded = _Upgrade_to_double<_Ty>; + const auto _Val = static_cast<_Upgraded>(_Left); + + return _Val; } template , int> = 0> _NODISCARD _CONSTEXPR20 _Upgrade_to_double<_Ty> norm(_Ty _Left) { - return static_cast<_Upgrade_to_double<_Ty>>(_Left * _Left); + using _Upgraded = _Upgrade_to_double<_Ty>; + const auto _Val = static_cast<_Upgraded>(_Left); + + return _Val * _Val; } template , int> = 0> -_NODISCARD _CONSTEXPR20 _Upgrade_to_double<_Ty> conj(_Ty _Left) { - return static_cast<_Upgrade_to_double<_Ty>>(_Left); +_NODISCARD _CONSTEXPR20 complex<_Upgrade_to_double<_Ty>> conj(_Ty _Left) { + using _Upgraded = _Upgrade_to_double<_Ty>; + const auto _Val = static_cast<_Upgraded>(_Left); + + return complex<_Upgraded>(_Val, -_Upgraded{0}); } template , int> = 0> -_NODISCARD _Upgrade_to_double<_Ty> proj(_Ty _Left) { - return static_cast<_Upgrade_to_double<_Ty>>(_Left); +_NODISCARD complex<_Upgrade_to_double<_Ty>> proj(_Ty _Left) { + using _Upgraded = _Upgrade_to_double<_Ty>; + const auto _Val = static_cast<_Upgraded>(_Left); + + if (_Ctraits<_Upgraded>::_Isinf(_Val)) { + // C11 7.3.9.5/2: "z projects to z except that all complex infinities [...] + // project to positive infinity on the real axis." + return complex<_Upgraded>(_Ctraits<_Upgraded>::_Infv(), 0); + } + + return complex<_Upgraded>(_Val, 0); } // FUNCTION TEMPLATE pow diff --git a/tests/std/tests/Dev10_555491_complex_linker_errors/test.cpp b/tests/std/tests/Dev10_555491_complex_linker_errors/test.cpp index 781374770d4..a0fa5531ca4 100644 --- a/tests/std/tests/Dev10_555491_complex_linker_errors/test.cpp +++ b/tests/std/tests/Dev10_555491_complex_linker_errors/test.cpp @@ -2,12 +2,17 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include +#include #include +#include #include #include +#include using namespace std; +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + template T pseudo_bit_cast(const U& source) { static_assert(sizeof(T) == sizeof(U), ""); @@ -90,4 +95,36 @@ int main() { assert(nearly_equal_partwise(asinh(-1e+307 + 2e+307i), -708.3914896859491 + 1.1071487177940904i)); assert(nearly_equal_partwise(asinh(-2e+37f + 4e+37if), -87.386663f + 1.1071488if)); assert(nearly_equal_partwise(asinh(-1e+307L + 2e+307il), -708.3914896859491L + 1.1071487177940904il)); + + // Also test GH-785 ": std::arg does not work for negative real values" + + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v>); + STATIC_ASSERT(is_same_v>); + + assert((arg(-1.0) == arg(complex{-1.0, 0.0}))); + assert((arg(-1) == arg(complex{-1.0, 0.0}))); + + assert(imag(1729.0) == 0.0); + assert(imag(1729) == 0.0); + + assert(real(1729.0) == 1729.0); + assert(real(1729) == 1729.0); + + assert(norm(0x1p63) == 0x1p126); + assert(norm(0x8000'0000'0000'0000ULL) == 0x1p126); + + assert((conj(1729.0) == complex{1729.0, -0.0})); + assert((conj(1729) == complex{1729.0, -0.0})); + assert(signbit(conj(1729.0).imag())); + assert(signbit(conj(1729).imag())); + + assert((proj(1729.0) == complex{1729.0, 0.0})); + assert((proj(1729) == complex{1729.0, 0.0})); + constexpr double inf = numeric_limits::infinity(); + assert((proj(inf) == complex{inf, 0.0})); + assert((proj(-inf) == complex{inf, 0.0})); } From dfb9115277ca60757aaf9d10152d7ca2cb78ed69 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 5 May 2020 01:10:34 -0700 Subject: [PATCH 2/3] Test that float/double/long double aren't "upgraded". --- .../test.cpp | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/std/tests/Dev10_555491_complex_linker_errors/test.cpp b/tests/std/tests/Dev10_555491_complex_linker_errors/test.cpp index a0fa5531ca4..77e1fdbd773 100644 --- a/tests/std/tests/Dev10_555491_complex_linker_errors/test.cpp +++ b/tests/std/tests/Dev10_555491_complex_linker_errors/test.cpp @@ -105,6 +105,27 @@ int main() { STATIC_ASSERT(is_same_v>); STATIC_ASSERT(is_same_v>); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v>); + STATIC_ASSERT(is_same_v>); + + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v>); + STATIC_ASSERT(is_same_v>); + + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v); + STATIC_ASSERT(is_same_v>); + STATIC_ASSERT(is_same_v>); + assert((arg(-1.0) == arg(complex{-1.0, 0.0}))); assert((arg(-1) == arg(complex{-1.0, 0.0}))); From ad1d6fb858eb6c4d25060a5a675dc83e26444dda Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 5 May 2020 01:08:20 -0700 Subject: [PATCH 3/3] Tests pass for Clang, emit warnings for MSVC. --- tests/libcxx/expected_results.txt | 6 ++++-- tests/libcxx/skipped_tests.txt | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index b91c3caf449..5cb45fccb9a 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -597,9 +597,7 @@ std/numerics/complex.number/complex.special/float_long_double_implicit.compile.f std/re/re.traits/transform.pass.cpp FAIL # STL bug: Incorrect return types. -std/numerics/complex.number/cmplx.over/conj.pass.cpp FAIL std/numerics/complex.number/cmplx.over/pow.pass.cpp FAIL -std/numerics/complex.number/cmplx.over/proj.pass.cpp FAIL # STL bug: Missing assignment operators. std/numerics/numarray/template.mask.array/mask.array.assign/mask_array.pass.cpp FAIL @@ -846,6 +844,10 @@ std/iterators/predef.iterators/insert.iterators/back.insert.iterator/types.pass. std/iterators/predef.iterators/insert.iterators/front.insert.iterator/types.pass.cpp SKIPPED std/iterators/predef.iterators/insert.iterators/insert.iterator/types.pass.cpp SKIPPED +# Tests emit warning C4244: 'argument': conversion from 'T' to 'const std::complex::_Ty', possible loss of data +std/numerics/complex.number/cmplx.over/conj.pass.cpp:0 FAIL +std/numerics/complex.number/cmplx.over/proj.pass.cpp:0 FAIL + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures. diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 9f53e131880..dcba77ec788 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -597,9 +597,7 @@ numerics\complex.number\complex.special\float_long_double_implicit.compile.fail. re\re.traits\transform.pass.cpp # STL bug: Incorrect return types. -numerics\complex.number\cmplx.over\conj.pass.cpp numerics\complex.number\cmplx.over\pow.pass.cpp -numerics\complex.number\cmplx.over\proj.pass.cpp # STL bug: Missing assignment operators. numerics\numarray\template.mask.array\mask.array.assign\mask_array.pass.cpp @@ -846,6 +844,10 @@ iterators\predef.iterators\insert.iterators\back.insert.iterator\types.pass.cpp iterators\predef.iterators\insert.iterators\front.insert.iterator\types.pass.cpp iterators\predef.iterators\insert.iterators\insert.iterator\types.pass.cpp +# Tests emit warning C4244: 'argument': conversion from 'T' to 'const std::complex::_Ty', possible loss of data +numerics\complex.number\cmplx.over\conj.pass.cpp +numerics\complex.number\cmplx.over\proj.pass.cpp + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures.