#Unions
6 messages in this thread
I wold like to have a convenient way of handling double floats so that I
can refer to them either as a double float or as two long words, without
constant type-casting.
I have tried using:
#define High 0
#define Low 1
union DoubleFloat
{
double dp; // A double float number
long DP[2]; // The same number as two longs
};
struct DoubleFloat ValDPFloat;
but I find that ValDPFloat.DP[High] is stored _after_ ValDPFloat.dp
rather than at the same address, thus defeating the whole point.
I am using DICE. Is this a problem with DICE or with me?
Regards,
Shraddhan (via AP from Hertfordshire, England)
A further question, related to the above, is how do I define, initialise
and access an array of unions? I would like to have an array of double
floats, but I need to initialise them as pairs of longs, for the sake of
guaranteed precision to the last bit.
How would C know to which element of the union I was referring? -i.e the
pair of longs, or the double float?
Regards,
Shraddhan (via AP from Hertfordshire, England)
Hi again Shraddhan,
Declaring an array of unions and accessing the fields is the same as if it
were structures. For instance (see my other message for declaration of the
union):
union DoubleFloat df_array[10];
Initialising all elements to 1.0:
for (i = 0; i < 10; ++i)
{
df_array[i].dp = 1.0;
}
Or using the pair of longs:
for (i = 0; i < 10; ++i)
{
df_array[i].li[0] = 0x3ff00000;
df_array[i].li[1] = 0x00000000;
}
— Freddy
Hi Shraddhan –
When using static initialization on an array of unions, treat it like you
were initializing the first element of the union. There is no way in C to
statically initialize elements other than the first.
Also note that what you are doing with the union is technically illegal.
If you store a value in a given member of a union, it is undefined what
happens if you read from another element. Unions are intended to allow you
to save space by overlaying data items, not to "type pun" (as compiler
writers call it) by overlaying one type on another. The reason it's illegal
is that the compiler may decide to keep the value you are writing in a
register. It is allowed to do this as an optimization. When you read from
the other element of the union, you get whatever happens to be in memory
there, not what's in the register. There are situations with the SAS/C
optimizer that can cause this.
You'll be relatively safe if you declare your union with the "volatile"
keyword, but this will kill ALL optimizations having to do with it.
Personally, I'd use some macros:
#define LONGPTR(x) ((long *)&(x))
#define HIGH(x) (LONGPTR(x)[0])
#define LOW(x) (LONGPTR(x)[1])
This method has the disadvantage of only working on lvalues (variables) and
not on constants, of course. You can make it work for constants by a kludge:
declare an external double (here called "xxx") and do
#define HIGH(x) ((xxx=(x)), (LONGPTR(x)[0]))
#define LOW(x) ((xxx=(x)), (LONGPTR(x)[1]))
This takes advantage of the comma (,) operator, which simply evaluates its
first operand and performs any side effects, then evaluates its second
operand. The result of the operation is the value of the second operand.
–Doug
Hi Shraddhan,
The example _should_ work so I suspect an error in DICE.
Check out the following program (compiled with the GNU-compiler) and its
output:
#include <stdio.h>
union DoubleFloat
{
double dp;
long li[2];
};
union DoubleFloat df1;
int main (void)
{
printf ("sizeof (df1) : %2d address of df1 : %08p\n"
"sizeof (df1.dp): %2d address of df1.dp : %08p\n"
"sizeof (df1.li): %2d address of df1.li[0]: %08p\n"
" address of df1.li[1]: %08p\n",
sizeof (df1), &df1,
sizeof (df1.dp), &df1.dp,
sizeof (df1.li), &df1.li[0], &df1.li[1]);
df1.dp = 1.0;
printf ("%lf => %08lx %08lx\n", df1.dp, df1.li[0], df1.li[1]);
df1.li[0] = 0x3ff80000;
df1.li[1] = 0x00000000;
printf ("%08lx %08lx => %lf\n", df1.li[0], df1.li[1], df1.dp);
}
And the output:
sizeof (df1) : 8 address of df1 : 0x7d40ff4
sizeof (df1.dp): 8 address of df1.dp : 0x7d40ff4
sizeof (df1.li): 8 address of df1.li[0]: 0x7d40ff4
address of df1.li[1]: 0x7d40ff8
1.000000 => 3ff00000 00000000
3ff80000 00000000 => 1.500000
Which is the correct output (and the output you expect).
Hope this helps,
— Freddy
Shraddhan,
Somehow my reply yesterday seems to have got lost! The problem is not with
dice, but with your declaration of ValDPFloat. You have defined it as
struct DoubleFloat ValDPFloat;
whereas you should use
union DoubleFloat ValDPFloat;
Dice was taking your specification of DoubleFloat, and turning it into a
struct, thus making DP follow on from dp, rather than share the same memory
space
Mal
Binary Dreams