C99 complex numbers and aliasing

G

Glen Low

Suppose I have a complex number as follows:

double _Complex d;

Can I access the real part as

((double*) d) [0]

and the imaginary part as

((double*) d) [1] ?

The C99 seems to say yes in 6.25/13 which specifies the layout of
complex numbers, and possibly no in 6.5/7 which may rule out double
_Complex and double aliasing?

I'm trying to find a portable way of using gcc's __real__ and __imag__
extensions, which work as lvalues.

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com
 
K

Kevin Bracey

Suppose I have a complex number as follows:

double _Complex d;

Can I access the real part as

((double*) d) [0]

and the imaginary part as

((double*) d) [1] ?

The C99 seems to say yes in 6.25/13 which specifies the layout of
complex numbers, and possibly no in 6.5/7 which may rule out double
_Complex and double aliasing?

I don't think you're guaranteed to be able to do that portably, because of
6.5p7. But you can do

double r, i;
double _Complex d;

memcpy(&d, &r, sizeof(double))
memcpy((double *) &d + 1, &i, sizeof(double))

I believe. I think that's all the representation alignment actually
guarantees you by the letter of the standard.

An alternative thing to do to set just the real part, say, would be:

d = r + I*cimag(d);

If an idiom like that becomes standard, it is likely that compilers will spot
it and optimise it.
 
J

James Kuyper

Suppose I have a complex number as follows:

double _Complex d;

Can I access the real part as

((double*) d) [0]

and the imaginary part as

((double*) d) [1] ?

The C99 seems to say yes in 6.25/13 which specifies the layout of
complex numbers, and possibly no in 6.5/7 which may rule out double
_Complex and double aliasing?


The standard routinely uses "same representation and same alignment"
with the apparant intent of implying precisely that kind of
compatibility. However, it falls short of explicitly saying so. As a
practical matter, I think you can safely assume that "same
representation and same alignment" means precisely that, whether the
standard says so explicitly or not. The problem will be resolved
either by continuing to ignore it, or by adding explicit wording to
that effect; not by making such code non-portable.
 
M

Mark Piffer

Kevin Bracey said:
In message <[email protected]>
Suppose I have a complex number as follows:

double _Complex d;

Can I access the real part as

((double*) d) [0]

and the imaginary part as

((double*) d) [1] ?

I don't think you're guaranteed to be able to do that portably, because of
6.5p7. But you can do

double r, i;
double _Complex d;

memcpy(&d, &r, sizeof(double))
memcpy((double *) &d + 1, &i, sizeof(double))

Can you please explain why giving the "(double *) &d + 1" argument is
any different from the "((double*) d) [1]" access? (No critism meant,
really just asking). To me 6.5p6 sounds like memcpy too, only works on
objects without declared type, i.e. you haven't the right to access it
as a complex afterwards (does this imply that memcpy isn't doing any
kind of magic behind the curtain which a simple loop could not do?). I
wonder what I got wrong this time. These questions about
basic-as-can-be operations really make me wonder everytime if I should
have avoided usenet in the first place ;)

regards,
Mark
 
K

Kevin Bracey

Kevin Bracey said:
In message <[email protected]>
Suppose I have a complex number as follows:

double _Complex d;

Can I access the real part as

((double*) d) [0]

and the imaginary part as

((double*) d) [1] ?

I don't think you're guaranteed to be able to do that portably, because of
6.5p7. But you can do

double r, i;
double _Complex d;

memcpy(&d, &r, sizeof(double))
memcpy((double *) &d + 1, &i, sizeof(double))

Can you please explain why giving the "(double *) &d + 1" argument is
any different from the "((double*) d) [1]" access? (No critism meant,
really just asking).

It's down to aliasing rules. The compiler is free to assume that you don't
access objects as though they were different types. Thus, in

double complex cd;
double *pr = (double *) &cd;

cd = 2 + I;
*pr = 3;

The compiler is allowed to assume after this that cd still contains 2+I, as
it "knows" that a write to a double cannot alter an object of type complex
double. Paragraph 6.5p7 basically says that.

Now, those rules were originally designed to handle more clear-cut cases,
like writing to a float through an int * pointer. It's arguable that an
exception should be added to the list saying that the parts of a complex or
imaginary object can be accessed as its corresponding real type.

memcpy accesses objects as characters, thus is permitted by the last line of
6.5p7.
 
D

David R Tribble

Glen said:
Can I access the real part as
((double*) d) [0]
and the imaginary part as
((double*) d) [1] ?

Mark said:
Can you please explain why giving the "(double *) &d + 1" argument is
any different from the "((double*) d) [1]" access? (No critism meant,
really just asking).


These are equivalent:
((double *)&d)[1]
*((double *)&d + 1)

The original post is missing an '&', since 'd' is a complex double and
not a pointer.

-drt
 

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,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top