Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Aliasing in C99

47 views
Skip to first unread message

David Brown

unread,
May 31, 2012, 10:01:02 AM5/31/12
to
I am trying to figure out how to get aliasing to work correctly according
to the C99 rules. For example, converting between a float and its binary
representation.

float negPCast(float x) {
uint32_t u = *((uint32_t *) &x);
u ^= 0x80000000u;
return *((float *) &u);
}

In the absence of type-based aliasing, this will negate a float using
just a simple xor operation (ignore any issues with endianness, int
sizes, NaNs, etc., since this is just an example).


The pointer typecasting here will break strict aliasing rules, and is
therefore not valid C99. (I'm guessing that in this case, most compilers
will generate code that works as desired - but I'm looking for strictly
conforming methods.)


It is possible to re-implement it using type-punning unions:

float negUnion(float x) {
union { float f; uint32_t u; } uf;
uf.f = x;
uf.u ^= 0x80000000;
return uf.f;
}

This doesn't use pointer typecasting, but I believe type-punning unions
are undefined in C but implemented "properly" in most compilers.


It is also possible to use pointers to unions in casts:

float negUnionPCast(float x) {
typedef union { float f; uint32_t u; } UF;
uint32_t u = ((UF*) &x)->u;
u ^= 0x80000000u;
return ((UF*) &u)->f;
}

I /think/ pointer casts like this are not subject to strict aliasing
rules, but I don't know if the union usage is valid.


Does anyone know of other ways that are strictly valid and defined in
C99, and that also are efficient in use (I'd like to avoid things like
casting back and forth between char or char pointers, or volatile
accesses, etc.)?

James Kuyper

unread,
May 31, 2012, 10:21:50 AM5/31/12
to
On 05/31/2012 10:01 AM, David Brown wrote:
> I am trying to figure out how to get aliasing to work correctly according
> to the C99 rules. For example, converting between a float and its binary
> representation.
>
> float negPCast(float x) {
> uint32_t u = *((uint32_t *) &x);
> u ^= 0x80000000u;
> return *((float *) &u);
> }
>
> In the absence of type-based aliasing, this will negate a float using
> just a simple xor operation (ignore any issues with endianness, int
> sizes, NaNs, etc., since this is just an example).
>
>
> The pointer typecasting here will break strict aliasing rules, and is
> therefore not valid C99. (I'm guessing that in this case, most compilers
> will generate code that works as desired - but I'm looking for strictly
> conforming methods.)
>
>
> It is possible to re-implement it using type-punning unions:
>
> float negUnion(float x) {
> union { float f; uint32_t u; } uf;
> uf.f = x;
> uf.u ^= 0x80000000;
> return uf.f;
> }
>
> This doesn't use pointer typecasting, but I believe type-punning unions
> are undefined in C but implemented "properly" in most compilers.

The C99 standard explicitly clarifies (though only in a non-normative
footnote) that reading the value of a member different from the one last
written to results in reinterpretation of the bytes making up that
object according to the new type. In other words, it matches exactly
what most C compilers have done all along. As long as you're willing to
make a host of unportable assumptions about the representation of
'float' and 'uint32_t', your code should work.

> It is also possible to use pointers to unions in casts:
>
> float negUnionPCast(float x) {
> typedef union { float f; uint32_t u; } UF;
> uint32_t u = ((UF*) &x)->u;
> u ^= 0x80000000u;
> return ((UF*) &u)->f;
> }
>
> I /think/ pointer casts like this are not subject to strict aliasing
> rules, but I don't know if the union usage is valid.

If you had an actual union object, conversions of pointers to it into
pointers to the member types would be unproblematic. However, doing it
this way could be a problem. If, for instance, float has an alignment
requirement of 2, while uint32_t has an alignment requirement of 4, then
UF will also have an alignment requirement of 4. In that case &x might
not have the right alignment to allow conversion to UF*.

A union is allowed to have unnamed padding bytes. For instance, on a
64-bit machine, it might speed things up to pad this union to 64 bits,
allowing it to be accessed using 64-bit operations. "When a value is
stored in an object of structure or union type, including in a member
object, the bytes of the object representation that correspond to any
padding bytes take unspecified values." (6.2.6.1p6). This will be
problematic since *(UF*)&x is not an actual union object containing
padding bytes that you have permission to set.
--
James Kuyper

Eric Sosman

unread,
May 31, 2012, 10:52:36 AM5/31/12
to
On 5/31/2012 10:01 AM, David Brown wrote:
> I am trying to figure out how to get aliasing to work correctly according
> to the C99 rules. For example, converting between a float and its binary
> representation.
>
> float negPCast(float x) {
> uint32_t u = *((uint32_t *)&x);
> u ^= 0x80000000u;
> return *((float *)&u);
> }
>
> In the absence of type-based aliasing, this will negate a float using
> just a simple xor operation (ignore any issues with endianness, int
> sizes, NaNs, etc., since this is just an example).
>
>
> The pointer typecasting here will break strict aliasing rules, and is
> therefore not valid C99. (I'm guessing that in this case, most compilers
> will generate code that works as desired - but I'm looking for strictly
> conforming methods.)
>
>
> It is possible to re-implement it using type-punning unions:
>
> float negUnion(float x) {
> union { float f; uint32_t u; } uf;
> uf.f = x;
> uf.u ^= 0x80000000;
> return uf.f;
> }
>
> This doesn't use pointer typecasting, but I believe type-punning unions
> are undefined in C but implemented "properly" in most compilers.

6.5p7 lists the kinds of lvalues that can legitimately access
an object's stored value, and says in a footnote that the list is
meant to indicate when aliasing is and is not proper. One of the
proper lvalues is a union containing a member of a suitable type,
so it looks like the aliasing is permitted here (barring trap
representations and the like).

HOWEVER: Although `uf' is certainly an lvalue expression for
a suitable union, `uf.u' is not: It is an lvalue expression of the
type `uint32_t', which would not be a legitimate accessor for the
stored `float' value. There may have been an intent to allow
this, but I'm not convinced the language of 7.5p7 actually does so.
(BTW, I'm looking at N1256, not the final Standard.) Moving on...

6.5.2.3p3 has a footnote stating "If the member used to access
the contents of a union object is not the same as the member last
used to store a value in the object, the appropriate part of the
object representation of the value is reinterpreted as an object
representation in the new type [...]," which is exactly what you
want. Unfortunately, footnotes are non-normative, so you need to
find something in the normative text from which you can deduce what
the footnote is "clarifying." Perhaps a language lawyer can find
the right argument.

> It is also possible to use pointers to unions in casts:
>
> float negUnionPCast(float x) {
> typedef union { float f; uint32_t u; } UF;
> uint32_t u = ((UF*)&x)->u;
> u ^= 0x80000000u;
> return ((UF*)&u)->f;
> }
>
> I /think/ pointer casts like this are not subject to strict aliasing
> rules, but I don't know if the union usage is valid.

Looks like it should work if the plain-union approach works,
provided there aren't alignment issues and such.

> Does anyone know of other ways that are strictly valid and defined in
> C99, and that also are efficient in use (I'd like to avoid things like
> casting back and forth between char or char pointers, or volatile
> accesses, etc.)?

Using `char*' (or, better, `unsigned char*') certainly works,
per 6.5p7. I don't think `volatile' helps or hinders: It constrains
the sequence of operations, but not their validity.

--
Eric Sosman
eso...@ieee-dot-org.invalid

Xavier Roche

unread,
May 31, 2012, 10:53:24 AM5/31/12
to
On 05/31/2012 04:01 PM, David Brown wrote:
> It is also possible to use pointers to unions in casts:

This would break strict aliasing, too. You probably need an intermediate
union for that. Another way would be to only use union everywhere (on
x86-64 ABI, it will be stored in a integer register when passed as
argument to functions, so this won't make too much harm)

The unfortunate thing is that gcc, for example, will correctly handle
the integer part, but tend to copy temporarily to the stack the
register, and copy it back to the floating register. (gcc 4.3, Linux
ABI, x86-64)

Note: you may want to check out this excellent introduction to strict
aliasing here:
http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html

Xavier Roche

unread,
May 31, 2012, 10:55:33 AM5/31/12
to
On 05/31/2012 04:52 PM, Eric Sosman wrote:
> Using `char*' (or, better, `unsigned char*') certainly works,
> per 6.5p7.

Humm, my understanding was that two variables could have the same
address if of compatible types, which includes "char" and "anything
else". But here we would have three variables, two violating strict
aliasing rules. Am I missing something ?

Eric Sosman

unread,
May 31, 2012, 11:47:14 AM5/31/12
to
I'm not sure what you mean. "Two variables" cannot have the
same address unless their lifetimes are disjoint, regardless of
type compatibility:

int i, j;
double x, y;
assert(&i != &j && &x != &y && &i != &x && ...);

On the other hand, two members of the same union always have
the same address:

union { int i; double x; } u;
assert (&u.i == &u.x && &u == &u.x && ...);

On the gripping hand, "two variables" can share an address if
one is an array and the other is the array's [0] element, if you
consider the [0] element a "variable." (As far as I can see the
Standard doesn't use "variable" as a noun in normative text, so we
don't have a formal definition to help us out.)

6.5p7 lists the kinds of expressions that can access the
stored value of an object (whether it's a "variable" or not).
That's where "compatibility" comes in: You can access a stored
value via an lvalue of type compatible with the stored value's
effective type (see 6.5p6 for "effective type," which needs all
that explanation because of things like memcpy() which move
representations around without regard to types). And you can
access a stored value with lvalues whose types are "mildly"
incompatible, as in

unsigned int ui = 42;
int *ip = (int*) &ui;
assert (*ip == 42); // different signedness is OK
const unsigned int *uip = &ui;
assert (uip == 42); // different constness is OK

Finally, by long tradition you can always access the "object
representation" (6.2.6.1p4) of an addressable object by viewing
it as an array of bytes, so 6.5p7 lists "a character type" among
the kinds of lvalues permitted to access stored value.

--
Eric Sosman
eso...@ieee-dot-org.invalid

David Brown

unread,
May 31, 2012, 2:47:21 PM5/31/12
to
On Thu, 31 May 2012 16:53:24 +0200, Xavier Roche wrote:

> On 05/31/2012 04:01 PM, David Brown wrote:
>> It is also possible to use pointers to unions in casts:
>
> This would break strict aliasing, too. You probably need an intermediate
> union for that. Another way would be to only use union everywhere (on
> x86-64 ABI, it will be stored in a integer register when passed as
> argument to functions, so this won't make too much harm)
>
> The unfortunate thing is that gcc, for example, will correctly handle
> the integer part, but tend to copy temporarily to the stack the
> register, and copy it back to the floating register. (gcc 4.3, Linux
> ABI, x86-64)
>

Fortunately for me, x86 is the target I am least interested in (I do
embedded programming). But I think gcc does it this way because the
floating point data is in an SSE register of some sort, and can't easily
be moved into a normal register, and a "move to stack" followed by "read
from stack" is actually quite efficient because the data gets read back
from the processor's write queue.

> Note: you may want to check out this excellent introduction to strict
> aliasing here:
> http://cellperformance.beyond3d.com/articles/2006/06/understanding-
strict-aliasing.html

Yes, I've read that too. My main concern was that the type-punning union
suggestion was also undefined by C standards (but, according to the posts
I've read here, it is actually allowed by the notes in the standards).

Xavier Roche

unread,
May 31, 2012, 3:10:27 PM5/31/12
to
Le 31/05/2012 20:47, David Brown a écrit :
> But I think gcc does it this way because the
> floating point data is in an SSE register of some sort, and can't easily
> be moved into a normal register

Hmm, no, you can do that quite easily (movq %rax,%xmm0 for example).

I'm wondering if this is because gcc has troubles extracting pieces of
data inside a structure put in a register (structures of size <= 8 are
packed entirely in a register if I am correct on this architecture)

So I'll double check the produced code on other archs to be sure if I
were you :)

David Brown

unread,
May 31, 2012, 3:17:10 PM5/31/12
to
That is very helpful - basically, unions /are/ allowed for type punning
like this.

I'm not worried about assumptions about representations - I am working
with embedded compilers, so no real nasty systems (like mixed endian, 36-
bit integers, non-standard floats, ones-compliment arithmetic, etc.). If
my code needs to be portable across endianness, bitwidth, or other
details, I can handle it as required.

I just want to make sure that the code is correct by the standards, and
will continue to work even as newer compilers get smarter about their
optimisation. There is a lot of code (incorrectly) written using simple
pointer casts for such conversions which used to work fine, but now fails
with more aggressive optimisations. I'd like to avoid that in the future.


>> It is also possible to use pointers to unions in casts:
>>
>> float negUnionPCast(float x) {
>> typedef union { float f; uint32_t u; } UF; uint32_t u = ((UF*)
>> &x)->u;
>> u ^= 0x80000000u;
>> return ((UF*) &u)->f;
>> }
>>
>> I /think/ pointer casts like this are not subject to strict aliasing
>> rules, but I don't know if the union usage is valid.
>
> If you had an actual union object, conversions of pointers to it into
> pointers to the member types would be unproblematic. However, doing it
> this way could be a problem. If, for instance, float has an alignment
> requirement of 2, while uint32_t has an alignment requirement of 4, then
> UF will also have an alignment requirement of 4. In that case &x might
> not have the right alignment to allow conversion to UF*.
>

Again, the targets I use don't have a problem here. Alignment is always
the natural width of the object, or less if the bit width of the
processor is less. So floats and uint32_t have the same alignment.

I suppose there may be targets that can do unaligned accesses to 32-bit
integers but not floats, and you could force a pointer to an uint32_t
which was not 32-bit aligned. But I think anyone trying that hard to
cause problems for themselves deserves to succeed!

> A union is allowed to have unnamed padding bytes. For instance, on a
> 64-bit machine, it might speed things up to pad this union to 64 bits,
> allowing it to be accessed using 64-bit operations. "When a value is
> stored in an object of structure or union type, including in a member
> object, the bytes of the object representation that correspond to any
> padding bytes take unspecified values." (6.2.6.1p6). This will be
> problematic since *(UF*)&x is not an actual union object containing
> padding bytes that you have permission to set.

I didn't know the compiler could cause trouble with padding like this.
It is legal (AFAIK) to cast between a pointer to a type and a pointer to
a union containing that type. But I suppose not all accesses using these
pointers will be legal (just like you can cast an array to a pointer, but
cannot use the pointer to access data outside the array).

Tim Rentsch

unread,
May 31, 2012, 4:57:58 PM5/31/12
to
David Brown <david...@hesbynett.no> writes:

> I am trying to figure out how to get aliasing to work correctly according
> to the C99 rules. For example, converting between a float and its binary
> representation.
>
> float negPCast(float x) {
> uint32_t u = *((uint32_t *) &x);
> u ^= 0x80000000u;
> return *((float *) &u);
> }
>
> In the absence of type-based aliasing, this will negate a float using
> just a simple xor operation (ignore any issues with endianness, int
> sizes, NaNs, etc., since this is just an example).
>
>
> The pointer typecasting here will break strict aliasing rules, and is
> therefore not valid C99. (I'm guessing that in this case, most compilers
> will generate code that works as desired - but I'm looking for strictly
> conforming methods.)

Point of terminology: the appropriate term is "effective type"
rules, ie, this terminology is what the C Standard uses. The
term "strict aliasing" is a gcc-ism; the rules for "strict
aliasing" are similar to, but not exactly the same as, effective
type rules as the C Standard defines them.


> It is possible to re-implement it using type-punning unions:
>
> float negUnion(float x) {
> union { float f; uint32_t u; } uf;
> uf.f = x;
> uf.u ^= 0x80000000;
> return uf.f;
> }
>
> This doesn't use pointer typecasting, but I believe type-punning unions
> are undefined in C but implemented "properly" in most compilers.

Defined, not undefined. The specific behavior depends on the
representations of the types in question, but presumably these
representations are suitable on the systems you want to run on.


> It is also possible to use pointers to unions in casts:
>
> float negUnionPCast(float x) {
> typedef union { float f; uint32_t u; } UF;
> uint32_t u = ((UF*) &x)->u;
> u ^= 0x80000000u;
> return ((UF*) &u)->f;
> }
>
> I /think/ pointer casts like this are not subject to strict aliasing
> rules, but I don't know if the union usage is valid.

This approach gives undefined behavior, for several different
reasons, as others have explained. There's a good chance it will
work, but certainly that's not guaranteed under the Standard.


> Does anyone know of other ways that are strictly valid and defined in
> C99, and that also are efficient in use (I'd like to avoid things like
> casting back and forth between char or char pointers, or volatile
> accesses, etc.)?

The union method is well-defined (modulo the proviso about type
representations) and should work just fine. Since you are using
C99, this approach can be coded more directly using compound
literls, viz., (I am using 'unsigned' rather than 'uint32_t'
but they are the same on my system):

float
negate_float( float x ){
typedef union { float f; unsigned u; } UF;
return (UF){ .u = (UF){ x }.u ^ 0x80000000 }.f;
}

Compiling this function with gcc (using -O2 or -O3), the
generated code looks pretty good, about what I'd expect and also
as good as I think you would hope for. If an 'inline' qualifier
is added to the function definition, then generated code for a
call is just three instructions (this is on an x86), ie, load,
xor, store, and no floating point instructions.

Tim Rentsch

unread,
May 31, 2012, 5:21:18 PM5/31/12
to
Presumably you meant 6.5p7 in that penultimate sentence. (And these
paragraphs are unchanged between N1256 and N1570.)

The effective type of an object is defined in 6.5p6. The expression
doing the access is 'uf.u'. This expression falls under the first
sentence of 6.5p6, that is, 'uf.u' has a declared type, and the declared
type of 'uf.u' is the effective type for this access. Obviously the
declared type of 'uf.u' matches the type of the expression 'uf.u'
under 6.5p7, since they are the same type. Therefore the effective
type rules allow this access.


> 6.5.2.3p3 has a footnote stating "If the member used to access
> the contents of a union object is not the same as the member last
> used to store a value in the object, the appropriate part of the
> object representation of the value is reinterpreted as an object
> representation in the new type [...]," which is exactly what you
> want. Unfortunately, footnotes are non-normative, so you need to
> find something in the normative text from which you can deduce what
> the footnote is "clarifying." Perhaps a language lawyer can find
> the right argument.

I've posted on unions quite a few times over the last six months
or so, including citations to sections that define the behavior
(ie, in normative text) corresponding to this footnote. It isn't
hard to find the relevant passages if one is of a mind to do so.
Or, simply use google groups search to find my earlir posting on
the matter.

David Brown

unread,
May 31, 2012, 5:52:28 PM5/31/12
to
OK - this is the main point I've learned with this thread (and the reason
I asked in the first place). I know how to use such unions, I knew they
worked in practice - know I also know they work in theory (assuming, as
you say, the underlying representations are known).

>
>> It is also possible to use pointers to unions in casts:
>>
>> float negUnionPCast(float x) {
>> typedef union { float f; uint32_t u; } UF; uint32_t u = ((UF*)
>> &x)->u;
>> u ^= 0x80000000u;
>> return ((UF*) &u)->f;
>> }
>>
>> I /think/ pointer casts like this are not subject to strict aliasing
>> rules, but I don't know if the union usage is valid.
>
> This approach gives undefined behavior, for several different reasons,
> as others have explained. There's a good chance it will work, but
> certainly that's not guaranteed under the Standard.
>

OK. I'm not entirely confident about /why/ this is not correct according
to the standard, and it seems to be in conflict with other things I've
read. But either way, it leads to the same conclusion - it can't be
relied on to work, and so should not be used.

>
>> Does anyone know of other ways that are strictly valid and defined in
>> C99, and that also are efficient in use (I'd like to avoid things like
>> casting back and forth between char or char pointers, or volatile
>> accesses, etc.)?
>
> The union method is well-defined (modulo the proviso about type
> representations) and should work just fine. Since you are using C99,
> this approach can be coded more directly using compound literls, viz.,
> (I am using 'unsigned' rather than 'uint32_t' but they are the same on
> my system):
>
> float
> negate_float( float x ){
> typedef union { float f; unsigned u; } UF; return (UF){ .u =
> (UF){ x }.u ^ 0x80000000 }.f;
> }
>
> Compiling this function with gcc (using -O2 or -O3), the generated code
> looks pretty good, about what I'd expect and also as good as I think you
> would hope for. If an 'inline' qualifier is added to the function
> definition, then generated code for a call is just three instructions
> (this is on an x86), ie, load, xor, store, and no floating point
> instructions.

I like the idea, as it is an elegant and efficient solution. However,
I'm a big fan of clear and explicit code, and my code has to be easily
understood by others (even those not yet well versed in C99) - I think
this looks a bit convoluted for common use. But in some circumstances,
it could be the best solution.

With my brief testing (with gcc on amd64), all versions of the functions
gave the same code, including the movement of data onto the stack
mentioned elsewhere in the thread. But that's not a concern for me, as
that is not one of my targets.

Tim Rentsch

unread,
May 31, 2012, 9:15:14 PM5/31/12
to
David Brown <david...@hesbynett.no> writes:

> On Thu, 31 May 2012 13:57:58 -0700, Tim Rentsch wrote:
>
>> David Brown <david...@hesbynett.no> writes:
>> [snip]
>
> OK - this is the main point I've learned with this thread (and the reason
> I asked in the first place). I know how to use such unions, I knew they
> worked in practice - know I also know they work in theory (assuming, as
> you say, the underlying representations are known).

Good deal.


>>> It is also possible to use pointers to unions in casts:
>>>
>>> float negUnionPCast(float x) {
>>> typedef union { float f; uint32_t u; } UF; uint32_t u = ((UF*)
>>> &x)->u;
>>> u ^= 0x80000000u;
>>> return ((UF*) &u)->f;
>>> }
>>>
>>> I /think/ pointer casts like this are not subject to strict aliasing
>>> rules, but I don't know if the union usage is valid.
>>
>> This approach gives undefined behavior, for several different reasons,
>> as others have explained. There's a good chance it will work, but
>> certainly that's not guaranteed under the Standard.
>>
>
> OK. I'm not entirely confident about /why/ this is not correct according
> to the standard, and it seems to be in conflict with other things I've
> read. But either way, it leads to the same conclusion - it can't be
> relied on to work, and so should not be used.

For the same reason (among others) you were concerned in the
first place, ie, aliasing rules. You have something that is a
float, and in particular a float not in a union, and you access
it through a pointer to a union type! It's possible -- and I'm
not sure about this -- that the Standard does indeed allow this
under effective type rules. However, it's a grey area, and
because of that compilers may take liberties with what kinds of
optimizations that allow in such cases. In almost all cases
accessing something through a pointer converted to a type
not the same as that of the target is best avoided. There
also are alignment and padding byte issues, as others have
mentioned; no reason to start down that path when there
is another one that is easier and safer.


>>> Does anyone know of other ways that are strictly valid and defined in
>>> C99, and that also are efficient in use (I'd like to avoid things like
>>> casting back and forth between char or char pointers, or volatile
>>> accesses, etc.)?
>>
>> The union method is well-defined (modulo the proviso about type
>> representations) and should work just fine. Since you are using C99,
>> this approach can be coded more directly using compound literls, viz.,
>> (I am using 'unsigned' rather than 'uint32_t' but they are the same on
>> my system):
>>
>> float
>> negate_float( float x ){
>> typedef union { float f; unsigned u; } UF;
>> return (UF){ .u = (UF){ x }.u ^ 0x80000000 }.f;
>> }
>>
>> Compiling this function with gcc (using -O2 or -O3), the generated code
>> looks pretty good, about what I'd expect and also as good as I think you
>> would hope for. If an 'inline' qualifier is added to the function
>> definition, then generated code for a call is just three instructions
>> (this is on an x86), ie, load, xor, store, and no floating point
>> instructions.

[Note that the quoted function body had line-wrapping issues not
present in the original, which I have repaired above.]

> I like the idea, as it is an elegant and efficient solution. However,
> I'm a big fan of clear and explicit code, and my code has to be easily
> understood by others (even those not yet well versed in C99) - I think
> this looks a bit convoluted for common use. But in some circumstances,
> it could be the best solution.

I am also a fan of clear code. I suspect the issue here is
not lack of clarity but lack of familiarity; compound literals
were introduced in C99 and few people use them. So there is
something of a chicken and egg problem. However, if you
aren't comfortable using compound literals, we can still
write a simple function using a direct, functional style
(disclaimer: not compiled):

float
negate_float( float x ){
typedef union { unsigned u; float f; } UF;
const UF f = { .f = x }, u = { .u = f.u ^ 0x80000000 };
return u.f;
}

The type punning change from float to unsigned happens at 'f.u',
and from unsigned to float happens at 'u.f'. Taking a functional
approach allows the two union variables to be 'const'. Personally
I think this functional style is easier to understand than an
imperative one where a single union object is serving two different
purposes.


> With my brief testing (with gcc on amd64), all versions of the functions
> gave the same code, including the movement of data onto the stack
> mentioned elsewhere in the thread. But that's not a concern for me, as
> that is not one of my targets.

Another reason for writing this function using a functional style
rather than an updating assignment is that it's often easier for a
compiler to optimize such code, mapping as it does very
straightforwardly onto a single-assignment canonical form. Gcc
is pretty clever at optimizing, but for another compiler of
unknown abilities I think there is a better chance of it
optimizing nicely if this kind of functional approach is taken.

David Brown

unread,
Jun 1, 2012, 3:52:04 AM6/1/12
to
On 01/06/2012 03:15, Tim Rentsch wrote:
> David Brown<david...@hesbynett.no> writes:
>
>> On Thu, 31 May 2012 13:57:58 -0700, Tim Rentsch wrote:
>>
>>> David Brown<david...@hesbynett.no> writes:
>>> [snip]
>>
>> OK - this is the main point I've learned with this thread (and the reason
>> I asked in the first place). I know how to use such unions, I knew they
>> worked in practice - know I also know they work in theory (assuming, as
>> you say, the underlying representations are known).
>
> Good deal.
>
>
>>>> It is also possible to use pointers to unions in casts:
>>>>
>>>> float negUnionPCast(float x) {
>>>> typedef union { float f; uint32_t u; } UF; uint32_t u = ((UF*)
>>>> &x)->u;
>>>> u ^= 0x80000000u;
>>>> return ((UF*)&u)->f;
>>>> }
>>>>
>>>> I /think/ pointer casts like this are not subject to strict aliasing
>>>> rules, but I don't know if the union usage is valid.
>>>
>>> This approach gives undefined behavior, for several different reasons,
>>> as others have explained. There's a good chance it will work, but
>>> certainly that's not guaranteed under the Standard.
>>>
>>
>> OK. I'm not entirely confident about /why/ this is not correct according
>> to the standard, and it seems to be in conflict with other things I've
>> read. But either way, it leads to the same conclusion - it can't be
>> relied on to work, and so should not be used.
>
> For the same reason (among others) you were concerned in the
> first place, ie, aliasing rules. You have something that is a
> float, and in particular a float not in a union, and you access
> it through a pointer to a union type! It's possible -- and I'm
> not sure about this -- that the Standard does indeed allow this
> under effective type rules. However, it's a grey area, and
> because of that compilers may take liberties with what kinds of
> optimizations that allow in such cases. In almost all cases
> accessing something through a pointer converted to a type
> not the same as that of the target is best avoided. There
> also are alignment and padding byte issues, as others have
> mentioned; no reason to start down that path when there
> is another one that is easier and safer.

I my cases, I know that alignment and padding are not an issue. But I
fully agree that it is at best a grey area, and should therefore be
avoided. I was just saying that I'm not sure that it is a "black" area.
But as the whole point of my questions here was to find the safest
"white" areas, grey is out too!

Casts to unions are discussed in the gcc documentation as a gcc
extension, and are safe to use (carefully) with gcc - but while I often
use various gcc ports, I also use other compilers, so gcc extensions are
not a solution.

>
>
>>>> Does anyone know of other ways that are strictly valid and defined in
>>>> C99, and that also are efficient in use (I'd like to avoid things like
>>>> casting back and forth between char or char pointers, or volatile
>>>> accesses, etc.)?
>>>
>>> The union method is well-defined (modulo the proviso about type
>>> representations) and should work just fine. Since you are using C99,
>>> this approach can be coded more directly using compound literls, viz.,
>>> (I am using 'unsigned' rather than 'uint32_t' but they are the same on
>>> my system):
>>>
>>> float
>>> negate_float( float x ){
>>> typedef union { float f; unsigned u; } UF;
>>> return (UF){ .u = (UF){ x }.u ^ 0x80000000 }.f;
>>> }
>>>
>>> Compiling this function with gcc (using -O2 or -O3), the generated code
>>> looks pretty good, about what I'd expect and also as good as I think you
>>> would hope for. If an 'inline' qualifier is added to the function
>>> definition, then generated code for a call is just three instructions
>>> (this is on an x86), ie, load, xor, store, and no floating point
>>> instructions.
>
> [Note that the quoted function body had line-wrapping issues not
> present in the original, which I have repaired above.]

I noticed that too - sometimes mail/news programs mess up code formatting.

>
>> I like the idea, as it is an elegant and efficient solution. However,
>> I'm a big fan of clear and explicit code, and my code has to be easily
>> understood by others (even those not yet well versed in C99) - I think
>> this looks a bit convoluted for common use. But in some circumstances,
>> it could be the best solution.
>
> I am also a fan of clear code. I suspect the issue here is
> not lack of clarity but lack of familiarity; compound literals
> were introduced in C99 and few people use them. So there is
> something of a chicken and egg problem.

That's exactly the point - it's a matter of usage. I have used compound
literals occasionally, because sometimes it makes the code clearer, but
I have to do so with care so that the code is clear to people unused to
them.

> However, if you
> aren't comfortable using compound literals, we can still
> write a simple function using a direct, functional style
> (disclaimer: not compiled):
>
> float
> negate_float( float x ){
> typedef union { unsigned u; float f; } UF;
> const UF f = { .f = x }, u = { .u = f.u ^ 0x80000000 };
> return u.f;
> }
>

I'd split the line in two:
const UF f = (UF) { .f = x };
const UF u = (UF) { .u = f.u ^ 0x80000000 };

Then I think it is clear even to people unfamiliar with compound literals.

> The type punning change from float to unsigned happens at 'f.u',
> and from unsigned to float happens at 'u.f'. Taking a functional
> approach allows the two union variables to be 'const'. Personally
> I think this functional style is easier to understand than an
> imperative one where a single union object is serving two different
> purposes.
>
>
>> With my brief testing (with gcc on amd64), all versions of the functions
>> gave the same code, including the movement of data onto the stack
>> mentioned elsewhere in the thread. But that's not a concern for me, as
>> that is not one of my targets.
>
> Another reason for writing this function using a functional style
> rather than an updating assignment is that it's often easier for a
> compiler to optimize such code, mapping as it does very
> straightforwardly onto a single-assignment canonical form. Gcc
> is pretty clever at optimizing, but for another compiler of
> unknown abilities I think there is a better chance of it
> optimizing nicely if this kind of functional approach is taken.

Exactly - writing clear and straightforward code gives the compiler the
best chance.

Thanks for your suggestions and comments.

mvh.,

David

Tim Rentsch

unread,
Jun 1, 2012, 12:07:31 PM6/1/12
to
David Brown <da...@westcontrol.removethisbit.com> writes:

> On 01/06/2012 03:15, Tim Rentsch wrote:
>> David Brown<david...@hesbynett.no> writes:
>>
>>> On Thu, 31 May 2012 13:57:58 -0700, Tim Rentsch wrote:
>>>
>>>> David Brown<david...@hesbynett.no> writes:
[several snips done in the following, for compactness]

>>> [on the matter of using a casted pointer]
>>
>> For the same reason (among others) you were concerned in the
>> first place, ie, aliasing rules. You have something that is a
>> float, and in particular a float not in a union, and you access
>> it through a pointer to a union type!

I should have been more specific about my reaction. The wording
of the effective type rules is rather clumsy. I think it's hard
to make a convincing argument either way, based just on that
wording and nothing else. Despite that, I think it's reasonable
to make an educated guess as to the intention behind what was
actually written, and that would go like this: on the one hand
we have a simple variable, ie, not in a union (or struct), and on
the other hand we have an access to a member of a union (struct
member access would be equivalent); we know that the standalone
variable cannot possibly be in a struct or union, whereas member
access _must_ refer to an actual struct or union object, and
therefore the two objects in question must be distinct, ie, no
aliasing can definedly occur between them.

To repeat myself, I wouldn't call this an ironclad argument,
reasoning as it does based somewhat on speculating about the
underlying intention. However, I think the basic reasoning
is convincing enough so that some implementations might take
the same view, and that's why I think it's a grey area, and
consequently best avoided.

> Casts to unions are discussed in the gcc documentation as a gcc
> extension, and are safe to use (carefully) with gcc - but while I
> often use various gcc ports, I also use other compilers, so gcc
> extensions are not a solution.

Even if casts to unions were universally available, casting
a pointer to a non-compound type (ie, not a struct or union)
to a pointer to a compound type is a totally different beast,
because casting to a union (or struct) operates on values,
whereas pointer casting implicitly operates on object
representations and may have resulting aliasing issues.
Casting that works on values can never, in and of itself,
have even potential aliasing issues; casting that works
on pointers always can.


>> However, if you
>> aren't comfortable using compound literals, we can still
>> write a simple function using a direct, functional style
>> (disclaimer: not compiled):
>>
>> float
>> negate_float( float x ){
>> typedef union { unsigned u; float f; } UF;
>> const UF f = { .f = x }, u = { .u = f.u ^ 0x80000000 };
>> return u.f;
>> }
>>
>
> I'd split the line in two:
> const UF f = (UF) { .f = x };
> const UF u = (UF) { .u = f.u ^ 0x80000000 };

I'm fine with either one line or two. Any developer worth his
salt should be able to read either form with no difficulty, and
I think it's wrong to be overly dogmatic about a "one declaration
per line" rule. That said, the specific case here is (for me at
least) below the threshold of arguing one way or another.

Incidentally, it's usually a good rule in newsgroup postings
to use multiple spaces rather than tabs for indentation (and
for that matter all other uses too).


> Then I think it is clear even to people unfamiliar with
> compound literals.

Note that the function body I wrote did not use compound
literals, but just regular initialization. Your two lines
above could (and perhaps should) have been written thusly:

const UF f = { .f = x };
const UF u = { .u = f.u ^ 0x80000000 };

I expect most developers would prefer this writing to the
earlier alternative.

If compound literals are okay for your audience, it seems
natural to avoid the temporary variable 'f', which is used
in only one place; that would allow one declaration instead
of two:

const UF u = { .u = 0x80000000 ^ (UF){ .f = x }.u };

But then I think you know where this is going. :)


> Thanks for your suggestions and comments.

You're welcome, it's good to know they have been helpful.
0 new messages