From 4f75543dc417cee2db2fc46f516f7e3137e4b250 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Wed, 1 May 2019 01:08:36 +0100 Subject: [PATCH] PR libstdc++/61761 fix std::proj for targets without C99 cproj The current generic implementation of __complex_proj used when cproj is not available calculates the wrong projection, giving a different result than given by C99's cproj. When C99 cproj is not available but isinf and copysign are, use those to give correct results for float, double and long double. Otherwise, and for other specializations of std::complex, just use a generic version that returns its argument, and so doesn't support infinities. We might want to consider adding additional overloads of __complex_proj to support extended types such as _Float64x, _Float128 etc. PR libstdc++/61761 * include/std/complex (__complex_proj): Return parameter unchanged. [_GLIBCXX_USE_C99_COMPLEX] (__complex_proj): Change overloads for floating-point types to take std::complex arguments. [_GLIBCXX_USE_C99_MATH_TR1] (__complex_proj): Add overloads for floating-point types. * testsuite/26_numerics/complex/proj.cc: New test. From-SVN: r270759 --- libstdc++-v3/ChangeLog | 10 + libstdc++-v3/include/std/complex | 68 +-- .../testsuite/26_numerics/complex/proj.cc | 387 ++++++++++++++++++ 3 files changed, 440 insertions(+), 25 deletions(-) create mode 100644 libstdc++-v3/testsuite/26_numerics/complex/proj.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 9b076846598..5a070ff79c9 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,13 @@ +2019-05-01 Jonathan Wakely + + PR libstdc++/61761 + * include/std/complex (__complex_proj): Return parameter unchanged. + [_GLIBCXX_USE_C99_COMPLEX] (__complex_proj): Change overloads for + floating-point types to take std::complex arguments. + [_GLIBCXX_USE_C99_MATH_TR1] (__complex_proj): Add overloads for + floating-point types. + * testsuite/26_numerics/complex/proj.cc: New test. + 2019-04-30 Jakub Jelinek * config/abi/pre/gnu.ver (GLIBCXX_3.4.26): Change _Lock_policyE2 exports diff --git a/libstdc++-v3/include/std/complex b/libstdc++-v3/include/std/complex index 0a4f68bc438..45450e8ca01 100644 --- a/libstdc++-v3/include/std/complex +++ b/libstdc++-v3/include/std/complex @@ -1898,41 +1898,59 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template std::complex<_Tp> proj(const std::complex<_Tp>&); + // Generic implementation of std::proj, does not work for infinities. template - std::complex<_Tp> + inline std::complex<_Tp> __complex_proj(const std::complex<_Tp>& __z) - { - const _Tp __den = (__z.real() * __z.real() - + __z.imag() * __z.imag() + _Tp(1.0)); - - return std::complex<_Tp>((_Tp(2.0) * __z.real()) / __den, - (_Tp(2.0) * __z.imag()) / __den); - } + { return __z; } #if _GLIBCXX_USE_C99_COMPLEX - inline __complex__ float - __complex_proj(__complex__ float __z) - { return __builtin_cprojf(__z); } - - inline __complex__ double - __complex_proj(__complex__ double __z) - { return __builtin_cproj(__z); } - - inline __complex__ long double - __complex_proj(const __complex__ long double& __z) - { return __builtin_cprojl(__z); } + inline complex + __complex_proj(const complex& __z) + { return __builtin_cprojf(__z.__rep()); } + + inline complex + __complex_proj(const complex& __z) + { return __builtin_cproj(__z.__rep()); } + + inline complex + __complex_proj(const complex& __z) + { return __builtin_cprojl(__z.__rep()); } +#elif defined _GLIBCXX_USE_C99_MATH_TR1 + inline complex + __complex_proj(const complex& __z) + { + if (__builtin_isinf(__z.real()) || __builtin_isinf(__z.imag())) + return complex(__builtin_inff(), + __builtin_copysignf(0.0f, __z.imag())); + return __z; + } + + inline complex + __complex_proj(const complex& __z) + { + if (__builtin_isinf(__z.real()) || __builtin_isinf(__z.imag())) + return complex(__builtin_inf(), + __builtin_copysign(0.0, __z.imag())); + return __z; + } + + inline complex + __complex_proj(const complex& __z) + { + if (__builtin_isinf(__z.real()) || __builtin_isinf(__z.imag())) + return complex(__builtin_infl(), + __builtin_copysignl(0.0l, __z.imag())); + return __z; + } +#endif - template - inline std::complex<_Tp> - proj(const std::complex<_Tp>& __z) - { return __complex_proj(__z.__rep()); } -#else template inline std::complex<_Tp> proj(const std::complex<_Tp>& __z) { return __complex_proj(__z); } -#endif + // Overload for scalars template inline std::complex::__type> proj(_Tp __x) diff --git a/libstdc++-v3/testsuite/26_numerics/complex/proj.cc b/libstdc++-v3/testsuite/26_numerics/complex/proj.cc new file mode 100644 index 00000000000..b70ca4c58e9 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/complex/proj.cc @@ -0,0 +1,387 @@ +// 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-do run } + +#include +#include +#include + +template +bool eq(const std::complex& x, const std::complex& y) +{ + bool nan_reals = std::isnan(x.real()) && std::isnan(y.real()); + bool nan_imags = std::isnan(x.imag()) && std::isnan(y.imag()); + + bool sign_reals + = std::copysign(T(1), x.real()) == std::copysign(T(1), y.real()); + bool sign_imags + = std::copysign(T(1), x.imag()) == std::copysign(T(1), y.imag()); + + return ((x.real() == y.real() && sign_reals) || nan_reals) + && ((x.imag() == y.imag() && sign_imags) || nan_imags); +} + +void +test01() +{ + const double qnan = std::numeric_limits::quiet_NaN(); + const double pinf = std::numeric_limits::infinity(); + const double ninf = -pinf; + + std::complex c00(0, 0); + VERIFY( eq( std::proj(c00) , c00 ) ); + VERIFY( eq( std::proj(-c00) , -c00 ) ); + c00.real(-0.0); + VERIFY( eq( std::proj(c00) , c00 ) ); + VERIFY( eq( std::proj(-c00) , -c00 ) ); + + const std::complex c01(0, 1); + VERIFY( eq( std::proj(c01) , c01 ) ); + VERIFY( eq( std::proj(-c01) , -c01 ) ); + c00.real(-0.0); + VERIFY( eq( std::proj(c01) , c01 ) ); + VERIFY( eq( std::proj(-c01) , -c01 ) ); + + const std::complex c10(1, 0); + VERIFY( eq( std::proj(c10) , c10 ) ); + VERIFY( eq( std::proj(-c10) , -c10 ) ); + + const std::complex c12(1, 2); + VERIFY( eq( std::proj(c12) , c12 ) ); + VERIFY( eq( std::proj(-c12) , -c12 ) ); + + const std::complex c0q(0, qnan); + VERIFY( eq( std::proj(c0q) , c0q ) ); + VERIFY( eq( std::proj(-c0q) , -c0q ) ); + + const std::complex c1q(1, qnan); + VERIFY( eq( std::proj(c1q) , c1q ) ); + VERIFY( eq( std::proj(-c1q) , -c1q ) ); + + const std::complex cq0(qnan, 0); + VERIFY( eq( std::proj(cq0) , cq0 ) ); + VERIFY( eq( std::proj(-cq0) , -cq0 ) ); + + const std::complex cq1(qnan, 1); + VERIFY( eq( std::proj(cq1) , cq1 ) ); + VERIFY( eq( std::proj(-cq1) , -cq1 ) ); + + const std::complex cqq(qnan, qnan); + VERIFY( eq( std::proj(cqq) , cqq ) ); + VERIFY( eq( std::proj(-cqq) , -cqq ) ); + + const std::complex c0p(0, pinf); + VERIFY( eq( std::proj(c0p) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-c0p) , std::complex(pinf, -0.0) ) ); + + const std::complex c1p(1, pinf); + VERIFY( eq( std::proj(c1p) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-c1p) , std::complex(pinf, -0.0) ) ); + + const std::complex cqp(qnan, pinf); + VERIFY( eq( std::proj(cqp) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cqp) , std::complex(pinf, -0.0) ) ); + + const std::complex cpp(pinf, pinf); + VERIFY( eq( std::proj(cpp) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cpp) , std::complex(pinf, -0.0) ) ); + + const std::complex c0n(0, ninf); + VERIFY( eq( std::proj(c0n) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-c0n) , std::complex(pinf, +0.0) ) ); + + const std::complex c1n(1, ninf); + VERIFY( eq( std::proj(c1n) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-c1n) , std::complex(pinf, +0.0) ) ); + + const std::complex cqn(qnan, ninf); + VERIFY( eq( std::proj(cqn) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-cqn) , std::complex(pinf, +0.0) ) ); + + const std::complex cpn(pinf, ninf); + VERIFY( eq( std::proj(cpn) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-cpn) , std::complex(pinf, +0.0) ) ); + + const std::complex cnn(ninf, ninf); + VERIFY( eq( std::proj(cnn) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-cnn) , std::complex(pinf, +0.0) ) ); + + const std::complex cp0(pinf, 0); + VERIFY( eq( std::proj(cp0) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cp0) , std::complex(pinf, -0.0) ) ); + + const std::complex cp1(pinf, 1); + VERIFY( eq( std::proj(cp1) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cp1) , std::complex(pinf, -0.0) ) ); + + const std::complex cpq(pinf, qnan); + VERIFY( eq( std::proj(cpq) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cpq) , std::complex(pinf, -0.0) ) ); + + const std::complex cn0(ninf, 0); + VERIFY( eq( std::proj(cn0) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cn0) , std::complex(pinf, -0.0) ) ); + + const std::complex cn1(ninf, 1); + VERIFY( eq( std::proj(cn1) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cn1) , std::complex(pinf, -0.0) ) ); + + const std::complex cnq(ninf, qnan); + VERIFY( eq( std::proj(cnq) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cnq) , std::complex(pinf, -0.0) ) ); + + const std::complex cnp(ninf, pinf); + VERIFY( eq( std::proj(cnp) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cnp) , std::complex(pinf, -0.0) ) ); +} + +void +test02() +{ + const float qnan = std::numeric_limits::quiet_NaN(); + const float pinf = std::numeric_limits::infinity(); + const float ninf = -pinf; + + std::complex c00(0, 0); + VERIFY( eq( std::proj(c00) , c00 ) ); + VERIFY( eq( std::proj(-c00) , -c00 ) ); + c00.real(-0.0); + VERIFY( eq( std::proj(c00) , c00 ) ); + VERIFY( eq( std::proj(-c00) , -c00 ) ); + + const std::complex c01(0, 1); + VERIFY( eq( std::proj(c01) , c01 ) ); + VERIFY( eq( std::proj(-c01) , -c01 ) ); + c00.real(-0.0); + VERIFY( eq( std::proj(c01) , c01 ) ); + VERIFY( eq( std::proj(-c01) , -c01 ) ); + + const std::complex c10(1, 0); + VERIFY( eq( std::proj(c10) , c10 ) ); + VERIFY( eq( std::proj(-c10) , -c10 ) ); + + const std::complex c12(1, 2); + VERIFY( eq( std::proj(c12) , c12 ) ); + VERIFY( eq( std::proj(-c12) , -c12 ) ); + + const std::complex c0q(0, qnan); + VERIFY( eq( std::proj(c0q) , c0q ) ); + VERIFY( eq( std::proj(-c0q) , -c0q ) ); + + const std::complex c1q(1, qnan); + VERIFY( eq( std::proj(c1q) , c1q ) ); + VERIFY( eq( std::proj(-c1q) , -c1q ) ); + + const std::complex cq0(qnan, 0); + VERIFY( eq( std::proj(cq0) , cq0 ) ); + VERIFY( eq( std::proj(-cq0) , -cq0 ) ); + + const std::complex cq1(qnan, 1); + VERIFY( eq( std::proj(cq1) , cq1 ) ); + VERIFY( eq( std::proj(-cq1) , -cq1 ) ); + + const std::complex cqq(qnan, qnan); + VERIFY( eq( std::proj(cqq) , cqq ) ); + VERIFY( eq( std::proj(-cqq) , -cqq ) ); + + const std::complex c0p(0, pinf); + VERIFY( eq( std::proj(c0p) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-c0p) , std::complex(pinf, -0.0) ) ); + + const std::complex c1p(1, pinf); + VERIFY( eq( std::proj(c1p) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-c1p) , std::complex(pinf, -0.0) ) ); + + const std::complex cqp(qnan, pinf); + VERIFY( eq( std::proj(cqp) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cqp) , std::complex(pinf, -0.0) ) ); + + const std::complex cpp(pinf, pinf); + VERIFY( eq( std::proj(cpp) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cpp) , std::complex(pinf, -0.0) ) ); + + const std::complex c0n(0, ninf); + VERIFY( eq( std::proj(c0n) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-c0n) , std::complex(pinf, +0.0) ) ); + + const std::complex c1n(1, ninf); + VERIFY( eq( std::proj(c1n) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-c1n) , std::complex(pinf, +0.0) ) ); + + const std::complex cqn(qnan, ninf); + VERIFY( eq( std::proj(cqn) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-cqn) , std::complex(pinf, +0.0) ) ); + + const std::complex cpn(pinf, ninf); + VERIFY( eq( std::proj(cpn) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-cpn) , std::complex(pinf, +0.0) ) ); + + const std::complex cnn(ninf, ninf); + VERIFY( eq( std::proj(cnn) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-cnn) , std::complex(pinf, +0.0) ) ); + + const std::complex cp0(pinf, 0); + VERIFY( eq( std::proj(cp0) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cp0) , std::complex(pinf, -0.0) ) ); + + const std::complex cp1(pinf, 1); + VERIFY( eq( std::proj(cp1) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cp1) , std::complex(pinf, -0.0) ) ); + + const std::complex cpq(pinf, qnan); + VERIFY( eq( std::proj(cpq) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cpq) , std::complex(pinf, -0.0) ) ); + + const std::complex cn0(ninf, 0); + VERIFY( eq( std::proj(cn0) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cn0) , std::complex(pinf, -0.0) ) ); + + const std::complex cn1(ninf, 1); + VERIFY( eq( std::proj(cn1) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cn1) , std::complex(pinf, -0.0) ) ); + + const std::complex cnq(ninf, qnan); + VERIFY( eq( std::proj(cnq) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cnq) , std::complex(pinf, -0.0) ) ); + + const std::complex cnp(ninf, pinf); + VERIFY( eq( std::proj(cnp) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cnp) , std::complex(pinf, -0.0) ) ); +} + +void +test03() +{ + const long double qnan = std::numeric_limits::quiet_NaN(); + const long double pinf = std::numeric_limits::infinity(); + const long double ninf = -pinf; + + std::complex c00(0, 0); + VERIFY( eq( std::proj(c00) , c00 ) ); + VERIFY( eq( std::proj(-c00) , -c00 ) ); + c00.real(-0.0); + VERIFY( eq( std::proj(c00) , c00 ) ); + VERIFY( eq( std::proj(-c00) , -c00 ) ); + + const std::complex c01(0, 1); + VERIFY( eq( std::proj(c01) , c01 ) ); + VERIFY( eq( std::proj(-c01) , -c01 ) ); + c00.real(-0.0); + VERIFY( eq( std::proj(c01) , c01 ) ); + VERIFY( eq( std::proj(-c01) , -c01 ) ); + + const std::complex c10(1, 0); + VERIFY( eq( std::proj(c10) , c10 ) ); + VERIFY( eq( std::proj(-c10) , -c10 ) ); + + const std::complex c12(1, 2); + VERIFY( eq( std::proj(c12) , c12 ) ); + VERIFY( eq( std::proj(-c12) , -c12 ) ); + + const std::complex c0q(0, qnan); + VERIFY( eq( std::proj(c0q) , c0q ) ); + VERIFY( eq( std::proj(-c0q) , -c0q ) ); + + const std::complex c1q(1, qnan); + VERIFY( eq( std::proj(c1q) , c1q ) ); + VERIFY( eq( std::proj(-c1q) , -c1q ) ); + + const std::complex cq0(qnan, 0); + VERIFY( eq( std::proj(cq0) , cq0 ) ); + VERIFY( eq( std::proj(-cq0) , -cq0 ) ); + + const std::complex cq1(qnan, 1); + VERIFY( eq( std::proj(cq1) , cq1 ) ); + VERIFY( eq( std::proj(-cq1) , -cq1 ) ); + + const std::complex cqq(qnan, qnan); + VERIFY( eq( std::proj(cqq) , cqq ) ); + VERIFY( eq( std::proj(-cqq) , -cqq ) ); + + const std::complex c0p(0, pinf); + VERIFY( eq( std::proj(c0p) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-c0p) , std::complex(pinf, -0.0) ) ); + + const std::complex c1p(1, pinf); + VERIFY( eq( std::proj(c1p) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-c1p) , std::complex(pinf, -0.0) ) ); + + const std::complex cqp(qnan, pinf); + VERIFY( eq( std::proj(cqp) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cqp) , std::complex(pinf, -0.0) ) ); + + const std::complex cpp(pinf, pinf); + VERIFY( eq( std::proj(cpp) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cpp) , std::complex(pinf, -0.0) ) ); + + const std::complex c0n(0, ninf); + VERIFY( eq( std::proj(c0n) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-c0n) , std::complex(pinf, +0.0) ) ); + + const std::complex c1n(1, ninf); + VERIFY( eq( std::proj(c1n) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-c1n) , std::complex(pinf, +0.0) ) ); + + const std::complex cqn(qnan, ninf); + VERIFY( eq( std::proj(cqn) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-cqn) , std::complex(pinf, +0.0) ) ); + + const std::complex cpn(pinf, ninf); + VERIFY( eq( std::proj(cpn) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-cpn) , std::complex(pinf, +0.0) ) ); + + const std::complex cnn(ninf, ninf); + VERIFY( eq( std::proj(cnn) , std::complex(pinf, -0.0) ) ); + VERIFY( eq( std::proj(-cnn) , std::complex(pinf, +0.0) ) ); + + const std::complex cp0(pinf, 0); + VERIFY( eq( std::proj(cp0) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cp0) , std::complex(pinf, -0.0) ) ); + + const std::complex cp1(pinf, 1); + VERIFY( eq( std::proj(cp1) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cp1) , std::complex(pinf, -0.0) ) ); + + const std::complex cpq(pinf, qnan); + VERIFY( eq( std::proj(cpq) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cpq) , std::complex(pinf, -0.0) ) ); + + const std::complex cn0(ninf, 0); + VERIFY( eq( std::proj(cn0) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cn0) , std::complex(pinf, -0.0) ) ); + + const std::complex cn1(ninf, 1); + VERIFY( eq( std::proj(cn1) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cn1) , std::complex(pinf, -0.0) ) ); + + const std::complex cnq(ninf, qnan); + VERIFY( eq( std::proj(cnq) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cnq) , std::complex(pinf, -0.0) ) ); + + const std::complex cnp(ninf, pinf); + VERIFY( eq( std::proj(cnp) , std::complex(pinf, +0.0) ) ); + VERIFY( eq( std::proj(-cnp) , std::complex(pinf, -0.0) ) ); +} + +int +main() +{ + test01(); + test02(); + test03(); +} -- 2.30.2