Topic: Efficiency of the standard C++ complex class


Author: allan_w@my-dejanews.com (Allan W)
Date: Thu, 9 Jan 2003 22:07:23 +0000 (UTC)
Raw View
arajeev1@cs.umbc.edu (Rajeev Ayyagari) wrote
> (Just playing the Devil's advocate here.) 26.2.2/1 seems to say only
> that the complex class *can store* the Cartesian components... does
> this imply that the internal representation must consist of the real
> and imaginary components? I'm not sure.

It seems so to me, but I can see your point.

> Perhaps there are convoluted
> ways of storing the components that yield performance benefits for
> some specializations.

Even if that's true, the TC mentioned elsewhere on this thread will
change that. In other words, even if it doesn't have to be Cartesian
Coordinates today, it will have to tomorrow.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: allan_w@my-dejanews.com (Allan W)
Date: Thu, 9 Jan 2003 22:31:00 +0000 (UTC)
Raw View
cpdaniel@nospam.mvps.org ("Carl Daniel") wrote
> If you search the archives for this NG, and comp.lang.c++.moderated, you'll
> find numerous threads on this topic.  It seems that many numericists would
> prefer that the standard be modified to require that std::complex<T> be
> defined as
>
> template <class T>
> struct complex
> {
>     T    real;
>     T    imag;
> };
>
> This type of low-level knowledge of the internals of complex is essential
> for writing efficient versions of important numerical functions, like
> in-place real -> complex FFT.

Seems reasonable...

On the other hand, if their alternate definition is that trivial...
why not just use it as is? Why does the presence of std::complex
prevent them from using their own?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: pjp@dinkumware.com ("P.J. Plauger")
Date: Wed, 8 Jan 2003 00:30:11 +0000 (UTC)
Raw View
"Ian McCulloch" <ianmcc@lorentz.leidenuniv.nl> wrote in message
news:bac6d750.0301070722.74bac19c@posting.google.com...

> I would prefer to have an imaginary<double> type:
>
> z = 0.5 + 1.5 * I; // I is literal sqrt(-1) here
>
> Is there any other way to add a purely imaginary number to a complex
> number, without an extraneous (real_part = real_part + 0) operation?
>
> Even if there is a way to do this for addition, how do you _multiply_
> by complex<double>(0,1) ?  In IEEE arithmetic, multiplication of z by
> sqrt(-1) is well defined even if the real and/or imaginary parts of z
> are INF.  But INF*0 is NaN, so a self-respecting compiler shouldn't
> optimize away the multiplication, meaning that as well as being slow
> it will not give the intended answer.  The hand optimization z =
> complex<double>(-z.imag(), z.real()) is a non-answer.

See the C99 Standard, particularly the Annexes. It turns out that adding
pure imaginary types to the complex types raises all sorts of interesting
issues, to the point that C99 made pure imaginaries optional.

> IMHO real() and imag() shouldn't be functions at all, but public data
> members.  Since its too late to change it, makeing them return
> references is a sensible workaround.

In our library, we added real(const T&) and imag(const T&) to set the
individual components.

> I repeat my aboive question: given that cartesian representation is
> mandated, is there any reason NOT to further mandate layout
> compatibility with double[2]  (being a real,imaginary pair)?

No. In fact the C++ Committee has a Technical Corrigendum in the works
to that effect.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gdr@integrable-solutions.net (Gabriel Dos Reis)
Date: Wed, 8 Jan 2003 15:16:41 +0000 (UTC)
Raw View
ianmcc@lorentz.leidenuniv.nl (Ian McCulloch) writes:

| I repeat my aboive question: given that cartesian representation is
| mandated, is there any reason NOT to further mandate layout
| compatibility with double[2]  (being a real,imaginary pair)?

See

   http://anubis.dkuug.dk/JTC1/SC22/WG21/docs/papers/2002/1388.pdf

The committee agreed on the layout-compatibility proposal.

--
Gabriel Dos Reis, gdr@integrable-solutions.net

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: ian.mcculloch@wanadoo.nl (Ian McCulloch)
Date: Wed, 8 Jan 2003 15:17:06 +0000 (UTC)
Raw View
P.J. Plauger wrote:

> "Ian McCulloch" <ianmcc@lorentz.leidenuniv.nl> wrote in message
> news:bac6d750.0301070722.74bac19c@posting.google.com...
>
>> I would prefer to have an imaginary<double> type:

[snip]

>
> See the C99 Standard, particularly the Annexes. It turns out that adding
> pure imaginary types to the complex types raises all sorts of interesting
> issues, to the point that C99 made pure imaginaries optional.
>

Thanks, I'll look that up.

>> IMHO real() and imag() shouldn't be functions at all, but public data
>> members.  Since its too late to change it, makeing them return
>> references is a sensible workaround.
>
> In our library, we added real(const T&) and imag(const T&) to set the
> individual components.
>
>> I repeat my aboive question: given that cartesian representation is
>> mandated, is there any reason NOT to further mandate layout
>> compatibility with double[2]  (being a real,imaginary pair)?
>
> No. In fact the C++ Committee has a Technical Corrigendum in the works
> to that effect.

Great!  This makes it possible to write portable free-standing functions
set_real_part(complex<T>&, T) and set_imag_part(complex<T>&, T) which only
touch the real/imag parts respectively.  Given that you can do this the
ugly way, surely there should be a standard _clean_ way to do it?

Cheers,
Ian

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gdr@integrable-solutions.net (Gabriel Dos Reis)
Date: Wed, 8 Jan 2003 15:36:30 +0000 (UTC)
Raw View
ian.mcculloch@wanadoo.nl (Ian McCulloch) writes:

| >> I repeat my aboive question: given that cartesian representation is
| >> mandated, is there any reason NOT to further mandate layout
| >> compatibility with double[2]  (being a real,imaginary pair)?
| >
| > No. In fact the C++ Committee has a Technical Corrigendum in the works
| > to that effect.
|
| Great!  This makes it possible to write portable free-standing functions
| set_real_part(complex<T>&, T) and set_imag_part(complex<T>&, T) which only
| touch the real/imag parts respectively.  Given that you can do this the
| ugly way, surely there should be a standard _clean_ way to do it?

Sure and the link I gave you in another message just does that :-)

--
Gabriel Dos Reis, gdr@integrable-solutions.net

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: allan_w@my-dejanews.com (Allan W)
Date: Sun, 5 Jan 2003 18:35:42 +0000 (UTC)
Raw View
arajeev1@cs.umbc.edu (Rajeev Ayyagari) wrote
> I have posted this to comp.lang.c++; I was advised to post it to this
> group. The question of efficiency of the complex<> template has been
> raised before, but I cannot discover whether any modifications to the
> standard have solved the problem.
>
> Does anyone know of an efficient way to assign a complex number to a
> complex<double>?
>
> The two operator='s that are defined seem to be defined only for a
> real right hand side and for a complex<double> & right hand side. This
> means that in order to assign the complex number (0.5, 1.5) to a
> complex<double> z, I need to use a temporary, like so:
>
> complex<double> z;
> z = complex<double>( 0.5, 1.5 );

Does seem like rather an oversight, doesn't it?

> There *should* be a function called set() or something similar that
> allows code of the following sort:
>     z.set( 0.5 * i, 1.5 * i );

> I'm not sure whether an optimizing compiler would spot this and
> by-pass the creation of the temporary.

I don't think that the compiler could optimize away the temporary, unless
it knew for a fact that there were no side-effects lost. Conceptually
the specific implementation of complex could interact with the compiler
to make this possible, but I doubt if it's being done in real compilers
anywhere.

> In any case, having a method
> such as set() above would simplify things and not force programmers to
> rely on the optimizing capabilities of their compiler.

I think that the original designers probably wanted to make sure that
a complex<double> COULD be assigned, but perhaps missed the efficiency
angle this one time -- you're right, having to create a temprary
doesn't seem ideal, and a .set() member would cost almost nothing.

> An equivalent
> alternative is to have the real() and imag() member functions return
> references to the corresponding members.

That's an interesting idea.

Generally, the real and imaginary members are exactly the type of
implementation detail that encapsulation insists should be kept private.
In fact, it is literally a textbook case for it -- I'm quite sure that
I've read examples using some generic "complex" class (having nothing
to do with the standard). These texts generally explain that at some
point in the future, we might choose to keep the coordinates in polar
rather than Cartesian coordinates. If the internal data members are
kept private, this is simply a matter of updating the class and
recompiling; however, if the real and imaginary members have to be
present in Cartesian format, there is a great deal more work to be
done (identifying all code that relies on this and refactoring).

Also, as Scott Meyers says in one of his books, you should avoid
returning a "handle" (pointer or reference) to private data from a
public function -- because such a function is tantamount to making
that data public in the first place.

On the other hand, the standard already requires std::complex to
use Cartesian coordinates (26.2.2/1). Realistically (no pun intended),
there has to be two data elements of type T, which are returned by
real() and imag() explicitly. Changing these to return by reference
really wouldn't make the code more brittle.

> In summary:
> 1. Is there a way to efficiently assign a complex number (with a
> non-zero imaginary part) to a previously-instantiated complex<double>?

Not without non-standard hacks, as in

    double& inline realref(std::complex<double>&d) {
        // This non-standard hack might have to be changed
        // every time we port to a new compiler and/or library
        // Assumes std::complex<double> has no virtuals
        // Assumes that "real" is at offset 0 (BIG assumption)
        return static_cast<double*>(&d)[0];
    }
    double& inline imagref(std::complex<double>&d) {
        // This non-standard hack might have to be changed
        // every time we port to a new compiler and/or library
        // Assumes std::complex<double> has no virtuals
        // Assumes that "imag" is at offset sizeof(double) (BIG assumption)
        return static_cast<double*>(&d)[1];
    }
    void inline Set(std::complex<double>&d, real r, imag i)
        { realref(d)=r; imagref(d)=i; }

> 2. If not, is this a flaw in the design of the standard C++ complex
> class template?

I think you've made a convincing case!

> 3. Is there a good reason not to have a method such as set() above?

Seems easy enough to add.

> 4. Is it preferable to have real() and imag() return references to the
> corresponding members?

It's unusual, but perhaps warranted.

> Does this impose restrictions on the internal
> representation of the complex number?

Yes, but nothing draconian. Realistically those restrictions already
exist.

Probably wouldn't need to do both though... member set() and also
change real() and imag() to return references.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: cpdaniel@nospam.mvps.org ("Carl Daniel")
Date: Mon, 6 Jan 2003 03:10:52 +0000 (UTC)
Raw View
"Allan W" <allan_w@my-dejanews.com> wrote in message
news:7f2735a5.0301050309.21473172@posting.google.com...
> arajeev1@cs.umbc.edu (Rajeev Ayyagari) wrote
> > I'm not sure whether an optimizing compiler would spot this and
> > by-pass the creation of the temporary.
>
> I don't think that the compiler could optimize away the temporary, unless
> it knew for a fact that there were no side-effects lost. Conceptually
> the specific implementation of complex could interact with the compiler
> to make this possible, but I doubt if it's being done in real compilers
> anywhere.

FWIW, VC7.1:

std::complex<double> f;

void foo()
{
  f = std::complex<double>(1.0,2.0);
}

compiles foo() to:

?foo@@YAXXZ PROC NEAR     ; foo, COMDAT

; 7    :   f = std::complex<double>(1.0,2.0);

  00000 dd 05 00 00 00
 00   fld  QWORD PTR __real@3ff0000000000000
  00006 dd 1d 00 00 00
 00   fstp  QWORD PTR ?f@@3V?$complex@N@std@@A
  0000c dd 05 00 00 00
 00   fld  QWORD PTR __real@4000000000000000
  00012 dd 1d 08 00 00
 00   fstp  QWORD PTR ?f@@3V?$complex@N@std@@A+8

; 8    : }

  00018 c3   ret  0

Looks like it optimized away the temporary just fine.

> > 4. Is it preferable to have real() and imag() return references to the
> > corresponding members?
>
> It's unusual, but perhaps warranted.
>
> > Does this impose restrictions on the internal
> > representation of the complex number?
>
> Yes, but nothing draconian. Realistically those restrictions already
> exist.
>
> Probably wouldn't need to do both though... member set() and also
> change real() and imag() to return references.

If you search the archives for this NG, and comp.lang.c++.moderated, you'll
find numerous threads on this topic.  It seems that many numericists would
prefer that the standard be modified to require that std::complex<T> be
defined as

template <class T>
struct complex
{
    T    real;
    T    imag;
};

This type of low-level knowledge of the internals of complex is essential
for writing efficient versions of important numerical functions, like
in-place real -> complex FFT.

-cd

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: usenet_cpp@lehrerfamily.com (Joshua Lehrer)
Date: Mon, 6 Jan 2003 03:53:40 +0000 (UTC)
Raw View
allan_w@my-dejanews.com (Allan W) wrote in message news:<7f2735a5.0301050309.21473172@posting.google.com>...
> arajeev1@cs.umbc.edu (Rajeev Ayyagari) wrote
>
> > 3. Is there a good reason not to have a method such as set() above?
>
> Seems easy enough to add.
>

As an aside, I prefer "assign" for methods that have the same effect
as operator=, and have a parameter list corresponding to a
constructor.  I prefer "set" for setting single variables / data
members:

template <class T>
complex<T>& complex<T>::assign(const T& re_arg=0, const T&
imag_arg=0);

template <class T>
template <class X>
complex<T>& complex<T>::assign(const complex<X> &rhs);

These 2 assigns encompass all of the ways you can CONSTURCT a complex
object:

*c.assign(); == complex<T>();
c.assign(1); == complex<T>(1);
*c.assign(1,2); == complex<T>(1,2);
c.assign(complex<T>()); == complex<T>(complex<T>(1,2)); //copy
constructor
c.assign(complex<U>()); == complex<T>(complex<U>(1,2)); //templated
copy cnstr

The two marked with '*' can not be done with operator=, because
operator= takes 1 and only 1 argument.  The first would need to look
something like "c=;" which is not allowed.  The third, "c=1,2;" which
doesn't do what you think.

joshua lehrer
factset research systems

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: arajeev1@cs.umbc.edu (Rajeev Ayyagari)
Date: Tue, 7 Jan 2003 18:53:57 +0000 (UTC)
Raw View
usenet_cpp@lehrerfamily.com (Joshua Lehrer) wrote in message news:<31c49f0d.0301051747.17f258eb@posting.google.com>...

> As an aside, I prefer "assign" for methods that have the same effect
> as operator=, and have a parameter list corresponding to a
> constructor.  I prefer "set" for setting single variables / data
> members:

"assign()" does sound more natural than "set()" in this context.

Rajeev.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: ianmcc@lorentz.leidenuniv.nl (Ian McCulloch)
Date: Tue, 7 Jan 2003 18:53:57 +0000 (UTC)
Raw View
allan_w@my-dejanews.com (Allan W) wrote in message news:<7f2735a5.0301050309.21473172@posting.google.com>...
[snip]
> I think that the original designers probably wanted to make sure that
> a complex<double> COULD be assigned, but perhaps missed the efficiency
> angle this one time -- you're right, having to create a temprary
> doesn't seem ideal, and a .set() member would cost almost nothing.

I would prefer to have an imaginary<double> type:

z = 0.5 + 1.5 * I; // I is literal sqrt(-1) here

Is there any other way to add a purely imaginary number to a complex
number, without an extraneous (real_part = real_part + 0) operation?

Even if there is a way to do this for addition, how do you _multiply_
by complex<double>(0,1) ?  In IEEE arithmetic, multiplication of z by
sqrt(-1) is well defined even if the real and/or imaginary parts of z
are INF.  But INF*0 is NaN, so a self-respecting compiler shouldn't
optimize away the multiplication, meaning that as well as being slow
it will not give the intended answer.  The hand optimization z =
complex<double>(-z.imag(), z.real()) is a non-answer.

>
> > An equivalent
> > alternative is to have the real() and imag() member functions return
> > references to the corresponding members.
>
> That's an interesting idea.
>
> Generally, the real and imaginary members are exactly the type of
> implementation detail that encapsulation insists should be kept private.
> In fact, it is literally a textbook case for it -- I'm quite sure that
> I've read examples using some generic "complex" class (having nothing
> to do with the standard). These texts generally explain that at some
> point in the future, we might choose to keep the coordinates in polar
> rather than Cartesian coordinates. If the internal data members are
> kept private, this is simply a matter of updating the class and
> recompiling; however, if the real and imaginary members have to be
> present in Cartesian format, there is a great deal more work to be
> done (identifying all code that relies on this and refactoring).

Cartesian and polar representations differ in enough details that, if
you care about the numerics, you absolutely must specify in the
interface which is used.  Aside from having a big effect on speed,
that is the only way to get useable bounds on precision and range.  It
wouldn't be hard to write a function that depends on the
representation, so the encapsulation argument only goes so far.

Therefore, being able to change the internal representation is
therefore very useful, precisely (no pun indended) to see if the
numerics are sensitive to the change.  But, for the tiny amount of
code that cares, the choice is very important.

On the other hand, IMO it is pointless having a _standard_ complex
class unless it has layout compatibility with C's _Complex type (and
by extension, Fortran's complex type, on platforms which allow
interoperability).  Ability to change the underlying representation is
something that can be built on top of the standard version (with the
standard representation).  Going in the other direction (ie building
an interoperable complex type out of something that has an
unknown/changing representation) is more problematic.

>
> Also, as Scott Meyers says in one of his books, you should avoid
> returning a "handle" (pointer or reference) to private data from a
> public function -- because such a function is tantamount to making
> that data public in the first place.

For the purposes of the _standard_ complex class, the representation
of complex numbers as a contigous (real, imaginary) pair is so
universal that it really ought to be mandated.  Given that cartesian
representation is required, is anything lost by nailing down the
representation exactly?  (Specifying layout compatibility with
_Complex would be sufficent; I don't think any compiler vendors would
bother making a Fortran complex incompatible with a C _Complex).

>
> On the other hand, the standard already requires std::complex to
> use Cartesian coordinates (26.2.2/1). Realistically (no pun intended),
> there has to be two data elements of type T, which are returned by
> real() and imag() explicitly. Changing these to return by reference
> really wouldn't make the code more brittle.

I agree.

>
> > In summary:
> > 1. Is there a way to efficiently assign a complex number (with a
> > non-zero imaginary part) to a previously-instantiated complex<double>?
>
> Not without non-standard hacks, as in
>
>     double& inline realref(std::complex<double>&d) {
>         // This non-standard hack might have to be changed
>         // every time we port to a new compiler and/or library
>         // Assumes std::complex<double> has no virtuals
>         // Assumes that "real" is at offset 0 (BIG assumption)
>         return static_cast<double*>(&d)[0];
>     }
>     double& inline imagref(std::complex<double>&d) {
>         // This non-standard hack might have to be changed
>         // every time we port to a new compiler and/or library
>         // Assumes std::complex<double> has no virtuals
>         // Assumes that "imag" is at offset sizeof(double) (BIG assumption)
>         return static_cast<double*>(&d)[1];
>     }
>     void inline Set(std::complex<double>&d, real r, imag i)
>         { realref(d)=r; imagref(d)=i; }

I would argue that this should be explicitly mandated to work as
expected.  Otherwise, std::complex is useless for inter-language code.

>
> > 2. If not, is this a flaw in the design of the standard C++ complex
> > class template?
>
> I think you've made a convincing case!
>
> > 3. Is there a good reason not to have a method such as set() above?
>
> Seems easy enough to add.
>
> > 4. Is it preferable to have real() and imag() return references to the
> > corresponding members?
>
> It's unusual, but perhaps warranted.
>
> > Does this impose restrictions on the internal
> > representation of the complex number?
>
> Yes, but nothing draconian. Realistically those restrictions already
> exist.
>
> Probably wouldn't need to do both though... member set() and also
> change real() and imag() to return references.

I would prefer a genuine imaginary<T> class, which makes member set()
redundant I think.

IMHO real() and imag() shouldn't be functions at all, but public data
members.  Since its too late to change it, makeing them return
references is a sensible workaround.

I repeat my aboive question: given that cartesian representation is
mandated, is there any reason NOT to further mandate layout
compatibility with double[2]  (being a real,imaginary pair)?

Cheers,
Ian McCulloch

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: arajeev1@cs.umbc.edu (Rajeev Ayyagari)
Date: Tue, 7 Jan 2003 19:31:28 +0000 (UTC)
Raw View
allan_w@my-dejanews.com (Allan W) wrote in message news:<7f2735a5.0301050309.21473172@posting.google.com>...
> Also, as Scott Meyers says in one of his books, you should avoid
> returning a "handle" (pointer or reference) to private data from a
> public function -- because such a function is tantamount to making
> that data public in the first place.

I can see why, for more involved classes... once a non-const reference
is returned, it can be used to change internal state at will, without
the class' knowledge.

If the complex class provides members that return an involved function
of the complex number, it might want to cache the return values... in
which case returning a non-const reference would be a no-no. But I
don't think they're planning to do any such thing.

>
> On the other hand, the standard already requires std::complex to
> use Cartesian coordinates (26.2.2/1). Realistically (no pun intended),
> there has to be two data elements of type T, which are returned by
> real() and imag() explicitly. Changing these to return by reference
> really wouldn't make the code more brittle.
>

(Just playing the Devil's advocate here.) 26.2.2/1 seems to say only
that the complex class *can store* the Cartesian components... does
this imply that the internal representation must consist of the real
and imaginary components? I'm not sure. Perhaps there are convoluted
ways of storing the components that yield performance benefits for
some specializations.

Thanks!
Rajeev.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: arajeev1@cs.umbc.edu (Rajeev Ayyagari)
Date: Thu, 2 Jan 2003 06:38:06 +0000 (UTC)
Raw View
Hello All,

I have posted this to comp.lang.c++; I was advised to post it to this
group. The question of efficiency of the complex<> template has been
raised before, but I cannot discover whether any modifications to the
standard have solved the problem.

Does anyone know of an efficient way to assign a complex number to a
complex<double>?

The two operator='s that are defined seem to be defined only for a
real right hand side and for a complex<double> & right hand side. This
means that in order to assign the complex number (0.5, 1.5) to a
complex<double> z, I need to use a temporary, like so:

complex<double> z;
z = complex<double>( 0.5, 1.5 );

This becomes a problem if I'm using the complex<double> inside a loop,
but don't want to re-instantiate it each time. The code:

complex<double> z( 0, 0 );
for ( int i = 0; i < (1 << 30); i++ )
{
    z = complex<double>( 0.5 * i, 1.5 * i );
    // ... do some computation
}

is no more efficient than

for ( int i = 0; i < (1 << 30); i++ )
{
    complex<double> z( 0.5 * i, 1.5 * i );
    // ... do some computation
}

A complex<double> gets instantiated 2 << 30 times in both cases. It's
a temporary in the first case, and an explicit instantiation in the
second.

There *should* be a function called set() or something similar that
allows code of the following sort:

complex<double> z( 0, 0 );
for ( int i = 0; i < (1 << 30); i++ )
{
    z.set( 0.5 * i, 1.5 * i );
    // ... do some computation
}

This could be more efficient than instantiating and initializing z 1
<< 30 times.

I'm not sure whether an optimizing compiler would spot this and
by-pass the creation of the temporary. In any case, having a method
such as set() above would simplify things and not force programmers to
rely on the optimizing capabilities of their compiler. An equivalent
alternative is to have the real() and imag() member functions return
references to the corresponding members.

In summary:
1. Is there a way to efficiently assign a complex number (with a
non-zero imaginary part) to a previously-instantiated complex<double>?
2. If not, is this a flaw in the design of the standard C++ complex
class template?
3. Is there a good reason not to have a method such as set() above?
4. Is it preferable to have real() and imag() return references to the
corresponding members? Does this impose restrictions on the internal
representation of the complex number?

Thanks,
Rajeev.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]