arctan of complex number

M

Marc Schellens

Does anybody know an easy way to get the atan of a complex number in C++?
thanks,
marc
 
G

Gianni Mariani

Marc said:
Does anybody know an easy way to get the atan of a complex number in C++?
thanks,
marc

tan(cplx) = sin(cplx)/cos(cplx)

cplx= real + i*imag

sin(i*imag) = i * sinh(imag)

sinh(imag) = ( exp(imag) - exp(-imag) )/2

cos(i*imag) = cosh(imag)

cosh(imag) = ( exp(imag) + exp(-imag) )/2

then

sin(cplx) = sin(real)*cosh(imag) + sinh(imag)*cos(real)

cos(cplx) = cos(real)&cosh(imag) - sin(real)*sinh(imag)

so finally

tan( real + i * imag ) =
sin(real)*cosh(imag) + i * sinh(imag)*cos(real)
-----------------------------------------------
cos(real)&cosh(imag) - i * sin(real)*sinh(imag)


- this leads to this:

template <typename T>
std::complex<T> tan( const std::complex<T> & theta )
{
register T r = theta.real();
register T v = theta.imag();

T exp_v = exp(v);
T exp_mv = 1/exp_v; // same as exp(-v)

T cos_r = cos(r);
T cosh_v = ( exp_v + exp_mv ) / 2;

T sin_r = sin(r);
T sinh_v = ( exp_v - exp_mv ) / 2;

std::complex<T> numerator( sin_r*cosh_v, cos_r*sinh_v );

std::complex<T> denominator( cos_r*cosh_v, - sin_r*sinh_v );

return numerator / denominator;
}


- I didn't check it, I'll leave that exercise to you.

If performance is critical, I suspect that you can do better than this.
 
M

Marc Schellens

Gianni said:
tan(cplx) = sin(cplx)/cos(cplx)

cplx= real + i*imag

sin(i*imag) = i * sinh(imag)

sinh(imag) = ( exp(imag) - exp(-imag) )/2

cos(i*imag) = cosh(imag)

cosh(imag) = ( exp(imag) + exp(-imag) )/2

then

sin(cplx) = sin(real)*cosh(imag) + sinh(imag)*cos(real)

cos(cplx) = cos(real)&cosh(imag) - sin(real)*sinh(imag)

so finally

tan( real + i * imag ) =
sin(real)*cosh(imag) + i * sinh(imag)*cos(real)
-----------------------------------------------
cos(real)&cosh(imag) - i * sin(real)*sinh(imag)


- this leads to this:

template <typename T>
std::complex<T> tan( const std::complex<T> & theta )
{
register T r = theta.real();
register T v = theta.imag();

T exp_v = exp(v);
T exp_mv = 1/exp_v; // same as exp(-v)

T cos_r = cos(r);
T cosh_v = ( exp_v + exp_mv ) / 2;

T sin_r = sin(r);
T sinh_v = ( exp_v - exp_mv ) / 2;

std::complex<T> numerator( sin_r*cosh_v, cos_r*sinh_v );

std::complex<T> denominator( cos_r*cosh_v, - sin_r*sinh_v );

return numerator / denominator;
}


- I didn't check it, I'll leave that exercise to you.

If performance is critical, I suspect that you can do better than this.

But that (performance, elegance) was my point.
As Dan already suggested so helpfully, I could do it myself,
but I thought that somebody might have a tested performant solution.
Actually I suspected, that there is a standard 'hack' how to do it very
easyly in C++ (as its not in the STL).
Anyway, so far I came up myself with this:

// atan() for complex
template< typename C>
inline C atanC(const C& c)
{
const C i(0.0,1.0);
const C one(1.0,0.0);
return log( (one + i * c) / (one - i * c)) / (C(2.0,0.0)*i);
}

But thanks anyway,
marc
 
G

Gianni Mariani

Marc said:
Gianni Mariani wrote:
....

But that (performance, elegance) was my point.
As Dan already suggested so helpfully, I could do it myself,
but I thought that somebody might have a tested performant solution.
Actually I suspected, that there is a standard 'hack' how to do it very
easyly in C++ (as its not in the STL).
Anyway, so far I came up myself with this:

// atan() for complex
template< typename C>
inline C atanC(const C& c)
{
const C i(0.0,1.0);
const C one(1.0,0.0);
return log( (one + i * c) / (one - i * c)) / (C(2.0,0.0)*i);
}

OK - seems like I still need to learn to *read* - atan - not tan...hmmm.

I don't have my old texts on my shelf any more so I'll go with what
you've got (and it works ... atan( tan( c ) ) == c ). I suppose it's
easy enough to work out ...

template< typename T >
std::complex<T> atanC(const std::complex<T> & c)
{
register T real = c.real();
register T imag = c.imag();

std::complex<T> log_v =
log(
std::complex<T>( T(1) - imag, real )
/ std::complex<T>( T(1) + imag, - real )
);

return std::complex<T>(
log_v.imag() * T(1.0/2), - log_v.real() * T(1.0/2)
);
}

This one does one complex division and one "log(complex<T>)". Hence I
don't think you can make it much faster. A quick perf test shows that
it is about the modified one above is 30% faster (no inlining) than the
one in the original post.

A 1.2GHz AMD does 870K complex atan's per second.
 
M

Marc Schellens

Gianni said:
OK - seems like I still need to learn to *read* - atan - not tan...hmmm.

I don't have my old texts on my shelf any more so I'll go with what
you've got (and it works ... atan( tan( c ) ) == c ). I suppose it's
easy enough to work out ...

template< typename T >
std::complex<T> atanC(const std::complex<T> & c)
{
register T real = c.real();
register T imag = c.imag();

std::complex<T> log_v =
log(
std::complex<T>( T(1) - imag, real )
/ std::complex<T>( T(1) + imag, - real )
);

return std::complex<T>(
log_v.imag() * T(1.0/2), - log_v.real() * T(1.0/2)
);
}

This one does one complex division and one "log(complex<T>)". Hence I
don't think you can make it much faster. A quick perf test shows that
it is about the modified one above is 30% faster (no inlining) than the
one in the original post.

A 1.2GHz AMD does 870K complex atan's per second.

That looks quite optimized.
Thanks,
marc
 
G

Gianni Mariani

P.J. Plauger said:
It's fast all right, just not terribly accurate.


What are you referring to ? atan2 ? rounding errors ? better
polynomial approximation ?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top