Как использовать комплексные числа с boost::multiprecision::mpfr?

Согласно https://stackoverflow.com/a/17932632/1700939, должна быть возможность использовать комплексные числа с повышением :: многоточность с gcc-4.7. Это действительно отлично работает с boost::multiprecision::float128:

-----------test.cpp------------
#include <cmath>
#include <boost/multiprecision/float128.hpp>

using namespace std;

typedef boost::multiprecision::float128 real_type_b;
typedef complex<real_type_b> numeric_type_b;


int main()
{

    numeric_type_b a(2,2);
    numeric_type_b r = numeric_type_b(2,2)*a;
    a = sin(r);
    a = exp(r);
    cout << a << endl;

}
-----------test.cpp------------

$ g++-4.7 -std=c++0x test.cpp -lquadmath -o test
$ ./test
(-0.1455,0.989358)

Попытка сделать то же самое с boost::multiprecision::mpfr не работает для exp-функции (sin работает нормально (!))

-----------test.cpp------------
#include <cmath>
#include <boost/multiprecision/mpfr.hpp>

using namespace std;

typedef boost::multiprecision::mpfr_float_500 real_type_b;
typedef complex<real_type_b> numeric_type_b;


int main()
{

    numeric_type_b a(2,2);
    numeric_type_b r = numeric_type_b(2,2)*a;
    a = sin(r);
    a = exp(r);
    cout << a << endl;

}
-----------test.cpp------------

компиляция не удалась:

$ g++-4.7 -std=c++0x test.cpp -lmpfr -o test
In file included from /usr/include/boost/config/no_tr1/complex.hpp:21:0,
                from /usr/include/boost/math/policies/error_handling.hpp:15,
                from /usr/include/boost/multiprecision/detail/default_ops.hpp:9,
                from /usr/include/boost/multiprecision/detail/generic_interconvert.hpp:9,
                from /usr/include/boost/multiprecision/number.hpp:22,
                from /usr/include/boost/multiprecision/mpfr.hpp:9,
                from test.cpp:3:
/usr/include/c++/4.7/complex: In instantiation of ‘std::complex<_Tp> std::__complex_exp(const std::complex<_Tp>&) [with _Tp = boost::multiprecision::number<boost::multiprecision::backends::mpfr_float_backend<500u> >]’:
/usr/include/c++/4.7/complex:751:68:   required from ‘std::complex<_Tp> std::exp(const std::complex<_Tp>&) [with _Tp = boost::multiprecision::number<boost::multiprecision::backends::mpfr_float_backend<500u> >]’
test.cpp:17:18:   required from here
/usr/include/c++/4.7/complex:736:52: error: no matching function for call to ‘polar(boost::enable_if_c<true, boost::multiprecision::detail::expression<boost::multiprecision::detail::function, boost::multiprecision::detail::exp_funct<boost::multiprecision::backends::mpfr_float_backend<500u> >, boost::multiprecision::number<boost::multiprecision::backends::mpfr_float_backend<500u> >, void, void> >::type, boost::multiprecision::number<boost::multiprecision::backends::mpfr_float_backend<500u> >)’
/usr/include/c++/4.7/complex:736:52: note: candidate is:
/usr/include/c++/4.7/complex:662:5: note: template<class _Tp> std::complex<_Tp> std::polar(const _Tp&, const _Tp&)
/usr/include/c++/4.7/complex:662:5: note:   template argument deduction/substitution failed:
/usr/include/c++/4.7/complex:736:52: note:   deduced conflicting types for parameter ‘const _Tp’ (‘boost::multiprecision::detail::expression<boost::multiprecision::detail::function, boost::multiprecision::detail::exp_funct<boost::multiprecision::backends::mpfr_float_backend<500u> >, boost::multiprecision::number<boost::multiprecision::backends::mpfr_float_backend<500u> >, void, void>’ and ‘boost::multiprecision::number<boost::multiprecision::backends::mpfr_float_backend<500u> >’)

Однако, если строка exp закомментирована, это работает:

$ g++-4.7 -std=c++0x test.cpp -lmpfr -o test
$ ./test
(0,1490.48)

Я что-то делаю не так, или я столкнулся с ошибкой здесь?


person Sunday    schedule 12.02.2014    source источник
comment
Кажется, стоит сообщить об этом.   -  person Marc Glisse    schedule 12.02.2014
comment
Собственно, тоже libstdc++ IYAM. Размещение моего ответа   -  person sehe    schedule 13.02.2014
comment
+1 за отличный материал для вопросов. На моем g++ (4.8) мне пришлось перейти на std=c++03, потому что библиотека не нашла используемые литералы, определенные пользователем.   -  person sehe    schedule 13.02.2014
comment
Обратите внимание, что с libc++ llvm это происходит намного хуже.   -  person Marc Glisse    schedule 13.02.2014
comment
std::pow имеет ту же проблему (также в связи с полярным).   -  person Marc Glisse    schedule 13.02.2014
comment
@sehe Вероятно, потому что вы использовали -std=c++11 вместо -std=gnu++11, g++-4.9 печатает примечание после сообщения об ошибке, предлагающее это исправление.   -  person Marc Glisse    schedule 23.02.2014
comment
@MarcGlisse Спасибо. Приятно знать. (Хотя, я полагаю, это не совсем относится к теме вопроса)   -  person sehe    schedule 23.02.2014


Ответы (1)


Похоже на ошибку в библиотеке GNU. Он срабатывает, когда здесь есть _GLIBCXX_USE_C99_COMPLEX:

#if _GLIBCXX_USE_C99_COMPLEX
  // implementations

  template<typename _Tp>
    inline complex<_Tp>
    exp(const complex<_Tp>& __z) { return __complex_exp(__z.__rep()); }
#else
  template<typename _Tp>
    inline complex<_Tp>
    exp(const complex<_Tp>& __z) { return __complex_exp(__z); }
#endif

Похоже, он ломает ADL при вызове exp (путем переадресации со значением __rep()).

Вы можете легко убедиться в этом сами, жестко запрограммировав реализацию exp:

  template<typename T>
    inline std::complex<T>
    my_exp(const std::complex<T>& x)
    { 
        using std::exp; // use ADL
        return std::polar(exp(x.real()), x.imag()); 
    }

Это работает

#include <cmath>
#include <boost/multiprecision/float128.hpp>
#include <boost/multiprecision/mpfr.hpp>

#if 1
typedef boost::multiprecision::mpfr_float_500 real_type_b;
typedef std::complex<real_type_b> numeric_type_b;
#else
typedef boost::multiprecision::float128 real_type_b;
typedef std::complex<real_type_b> numeric_type_b;
#endif

namespace check
{
  template<typename T>
    inline std::complex<T>
    my_exp(const std::complex<T>& x)
    { 
        using std::exp; // use ADL
        T const& r = exp(x.real());
        return std::polar(r, x.imag()); 
    }
}

#include <iostream>

int main()
{
    numeric_type_b a(2,2);
    numeric_type_b r = numeric_type_b(2,2)*a;
    a = std::sin(r);
    a = check::my_exp(r);
    std::cout << a << std::endl;
}

Работает для обоих типов.

Выход

(-0.1455,0.989358)
person sehe    schedule 12.02.2014
comment
Спасибо за понимание. Может, стоит обратиться к какому-нибудь багтрекеру, чтобы починить? Я думаю, что не знаю достаточно деталей (пока), чтобы сделать это, но может пройти некоторое время, прежде чем я смогу копнуть глубже... - person Sunday; 27.02.2014
comment
Честно говоря, я думаю, это ваша работа — сообщить об этом. Я не думаю, что знаю больше, чем вы, я просто предоставил вам некоторую информацию. Кроме того, это никогда не было моей проблемой в первую очередь :) - person sehe; 27.02.2014
comment
(Уточнение: Люди не чувствуют себя в безопасности. Дело в том, что я точно так же не уверен в себе, но у меня нет вашей проблемы! Поэтому, если вы хотите сохранить хоть какой-то шанс, убедитесь, что заинтересованная сторона последует за вами. Если заинтересованные стороны не надо, никто не будет) - person sehe; 27.02.2014
comment
gcc.gnu.org/ml/gcc-patches/2014- 02/msg01377.html (т.е. будет работать в gcc-4.10, ждать чуть больше года). Если бы я еще не исправил это, да, было бы правильно сообщить об этом в gcc bugzilla. - person Marc Glisse; 04.03.2014