Home | Libraries | People | FAQ | More |
#include <boost/multiprecision/cpp_complex.hpp>
namespace boost{ namespace multiprecision{ template <unsigned Digits, backends::digit_base_type DigitBase = backends::digit_base_10, class Allocator = void, class Exponent = int, Exponent MinExponent = 0, Exponent MaxExponent = 0> using cpp_complex_backend = complex_adaptor<cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinExponent, MaxExponent> >; template <unsigned Digits, backends::digit_base_type DigitBase = digit_base_10, class Allocator = void, class Exponent = int, Exponent MinExponent = 0, Exponent MaxExponent = 0, expression_template_option ExpressionTemplates = et_off> using cpp_complex = number<complex_adaptor<cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinExponent, MaxExponent> >, ExpressionTemplates>; typedef cpp_complex<50> cpp_complex_50; typedef cpp_complex<100> cpp_complex_100; typedef cpp_complex<24, backends::digit_base_2, void, std::int16_t, -126, 127> cpp_complex_single; typedef cpp_complex<53, backends::digit_base_2, void, std::int16_t, -1022, 1023> cpp_complex_double; typedef cpp_complex<64, backends::digit_base_2, void, std::int16_t, -16382, 16383> cpp_complex_extended; typedef cpp_complex<113, backends::digit_base_2, void, std::int16_t, -16382, 16383> cpp_complex_quad; typedef cpp_complex<237, backends::digit_base_2, void, std::int32_t, -262142, 262143> cpp_complex_oct; }} // namespaces
The cpp_complex_backend
back-end is used in conjunction with number
:
It acts as an entirely C++ (header only and dependency free) complex number
type that is a drop-in replacement for std::complex
,
but with much greater precision.
The template alias cpp_complex
avoids the need to use class number
directly.
Type cpp_complex
can be
used at fixed precision by specifying a non-zero Digits
template parameter. The typedefs cpp_complex_50
and cpp_complex_100
provide
complex number types at 50 and 100 decimal digits precision respectively.
Optionally, you can specify whether the precision is specified in decimal
digits or binary bits - for example to declare a cpp_complex
with exactly the same precision as std::complex<double>
one would use cpp_complex<53,
digit_base_2>
.
The typedefs cpp_complex_single
,
cpp_complex_double
, cpp_complex_quad
, cpp_complex_oct
and cpp_complex_double_extended
provide software analogues of the IEEE single, double, quad and octuple
float data types, plus the Intel-extended-double type respectively. Note
that while these types are functionally equivalent to the native IEEE types,
but they do not have the same size or bit-layout as true IEEE compatible
types.
Normally cpp_complex
allocates
no memory: all of the space required for its digits are allocated directly
within the class. As a result care should be taken not to use the class
with too high a digit count as stack space requirements can grow out of
control. If that represents a problem then providing an allocator as a
template parameter causes cpp_complex
to dynamically allocate the memory it needs: this significantly reduces
the size of cpp_complex
and increases the viable upper limit on the number of digits at the expense
of performance. However, please bear in mind that arithmetic operations
rapidly become very expensive as the digit count grows:
the current implementation really isn't optimized or designed for large
digit counts. Note that since the actual type of the objects allocated
is completely opaque, the suggestion would be to use an allocator with
char
value_type
,
for example: cpp_complex<1000, digit_base_10, std::allocator<char> >
.
The next template parameters determine the type and range of the exponent:
parameter Exponent
can
be any signed integer type, but note that MinExponent
and MaxExponent
can not
go right up to the limits of the Exponent
type as there has to be a little extra headroom for internal calculations.
You will get a compile time error if this is the case. In addition if MinExponent
or MaxExponent are zero, then the library will choose suitable values that
are as large as possible given the constraints of the type and need for
extra headroom for internal calculations.
Finally, as with class number
,
the final template parameter determines whether expression templates are
turn on or not. Since by default this type allocates no memory, expression
template support is off by default. However, you should probably turn it
on if you specify an allocator.
There is full standard library support available for this type, comparable
with what std::complex
provides.
Things you should know when using this type:
cpp_complex
s
have a value of zero.
std::numeric_limits
specialisation for
this type: this is the same behaviour as std::complex
.
If you need std::numeric_limits
support you need to
look at std::numeric_limits<my_complex_number_type::value_type>
.
number
instantiated
on this type, is convertible to any other number
instantiated on this type - for example you can convert from number<cpp_complex<50> >
to number<cpp_bin_float<SomeOtherValue> >
.
Narrowing conversions round to nearest and are explicit
.
std::runtime_error
being thrown if the string can not be interpreted as a valid complex
number.
#include <iostream> #include <complex> #include <boost/multiprecision/cpp_complex.hpp> template<class Complex> void complex_number_examples() { Complex z1{0, 1}; std::cout << std::setprecision(std::numeric_limits<typename Complex::value_type>::digits10); std::cout << std::scientific << std::fixed; std::cout << "Print a complex number: " << z1 << std::endl; std::cout << "Square it : " << z1*z1 << std::endl; std::cout << "Real part : " << z1.real() << " = " << real(z1) << std::endl; std::cout << "Imaginary part : " << z1.imag() << " = " << imag(z1) << std::endl; using std::abs; std::cout << "Absolute value : " << abs(z1) << std::endl; std::cout << "Argument : " << arg(z1) << std::endl; std::cout << "Norm : " << norm(z1) << std::endl; std::cout << "Complex conjugate : " << conj(z1) << std::endl; std::cout << "Projection onto Riemann sphere: " << proj(z1) << std::endl; typename Complex::value_type r = 1; typename Complex::value_type theta = 0.8; using std::polar; std::cout << "Polar coordinates (phase = 0) : " << polar(r) << std::endl; std::cout << "Polar coordinates (phase !=0) : " << polar(r, theta) << std::endl; std::cout << "\nElementary special functions:\n"; using std::exp; std::cout << "exp(z1) = " << exp(z1) << std::endl; using std::log; std::cout << "log(z1) = " << log(z1) << std::endl; using std::log10; std::cout << "log10(z1) = " << log10(z1) << std::endl; using std::pow; std::cout << "pow(z1, z1) = " << pow(z1, z1) << std::endl; using std::sqrt; std::cout << "Take its square root : " << sqrt(z1) << std::endl; using std::sin; std::cout << "sin(z1) = " << sin(z1) << std::endl; using std::cos; std::cout << "cos(z1) = " << cos(z1) << std::endl; using std::tan; std::cout << "tan(z1) = " << tan(z1) << std::endl; using std::asin; std::cout << "asin(z1) = " << asin(z1) << std::endl; using std::acos; std::cout << "acos(z1) = " << acos(z1) << std::endl; using std::atan; std::cout << "atan(z1) = " << atan(z1) << std::endl; using std::sinh; std::cout << "sinh(z1) = " << sinh(z1) << std::endl; using std::cosh; std::cout << "cosh(z1) = " << cosh(z1) << std::endl; using std::tanh; std::cout << "tanh(z1) = " << tanh(z1) << std::endl; using std::asinh; std::cout << "asinh(z1) = " << asinh(z1) << std::endl; using std::acosh; std::cout << "acosh(z1) = " << acosh(z1) << std::endl; using std::atanh; std::cout << "atanh(z1) = " << atanh(z1) << std::endl; } int main() { std::cout << "First, some operations we usually perform with std::complex:\n"; complex_number_examples<std::complex<double>>(); std::cout << "\nNow the same operations performed using quad precision complex numbers:\n"; complex_number_examples<boost::multiprecision::cpp_complex_quad>(); return 0; }
Which produces the output (for the multiprecision type):
Print a complex number: (0.000000000000000000000000000000000,1.000000000000000000000000000000000) Square it : -1.000000000000000000000000000000000 Real part : 0.000000000000000000000000000000000 = 0.000000000000000000000000000000000 Imaginary part : 1.000000000000000000000000000000000 = 1.000000000000000000000000000000000 Absolute value : 1.000000000000000000000000000000000 Argument : 1.570796326794896619231321691639751 Norm : 1.000000000000000000000000000000000 Complex conjugate : (0.000000000000000000000000000000000,-1.000000000000000000000000000000000) Projection onto Riemann sphere: (0.000000000000000000000000000000000,1.000000000000000000000000000000000) Polar coordinates (phase = 0) : 1.000000000000000000000000000000000 Polar coordinates (phase !=0) : (0.696706709347165389063740022772448,0.717356090899522792567167815703377) Elementary special functions: exp(z1) = (0.540302305868139717400936607442977,0.841470984807896506652502321630299) log(z1) = (0.000000000000000000000000000000000,1.570796326794896619231321691639751) log10(z1) = (0.000000000000000000000000000000000,0.682188176920920673742891812715678) pow(z1, z1) = 0.207879576350761908546955619834979 Take its square root : (0.707106781186547524400844362104849,0.707106781186547524400844362104849) sin(z1) = (0.000000000000000000000000000000000,1.175201193643801456882381850595601) cos(z1) = 1.543080634815243778477905620757062 tan(z1) = (0.000000000000000000000000000000000,0.761594155955764888119458282604794) asin(z1) = (0.000000000000000000000000000000000,0.881373587019543025232609324979793) acos(z1) = (1.570796326794896619231321691639751,-0.881373587019543025232609324979793) atan(z1) = (0.000000000000000000000000000000000,inf) sinh(z1) = (0.000000000000000000000000000000000,0.841470984807896506652502321630299) cosh(z1) = 0.540302305868139717400936607442977 tanh(z1) = (0.000000000000000000000000000000000,1.557407724654902230506974807458360) asinh(z1) = (0.000000000000000000000000000000000,1.570796326794896619231321691639751) acosh(z1) = (0.881373587019543025232609324979792,1.570796326794896619231321691639751) atanh(z1) = (0.000000000000000000000000000000000,0.785398163397448309615660845819876)