Velocity Reviews > casting question - i64 = *((__int64*)&d);

# casting question - i64 = *((__int64*)&d);

Angus
Guest
Posts: n/a

 04-23-2008
I need to convert a double to a 64 bit integer. I am using __int64
but could of course use a more portable type.

To perform the correct casts:
double d = 3.3;
__int64 i64;
i64 = *((__int64*)&d);

//And to convert back
d = *((double*)&i64);

But I don't understand how this works. Can someone please explain?

Ben Bacarisse
Guest
Posts: n/a

 04-23-2008
Angus <(E-Mail Removed)> writes:

> I need to convert a double to a 64 bit integer. I am using __int64
> but could of course use a more portable type.
>
> To perform the correct casts:
> double d = 3.3;
> __int64 i64;
> i64 = *((__int64*)&d);
>
> //And to convert back
> d = *((double*)&i64);
>
> But I don't understand how this works. Can someone please explain?

You convert a pointer to the double -- &d -- to an __int64 pointer
-- (__int64 *)&d -- and then dereference that pointer to get the
64-bit integer that this converted pointer points to --
*(__int64 *)&d.

The code is not guaranteed to work (it is undefined behaviour) not
least because the resulting integer might be a trap representation.
Such code is very common. Much more portable is:

assert(sizeof d == sizeof i64);
memcpy(&i64, &d, sizeof d);

In fact the above is 100% portable, provided that one does not use the
resulting integer (which is often enough).

--
Ben.

jacob navia
Guest
Posts: n/a

 04-23-2008
Angus wrote:
> I need to convert a double to a 64 bit integer. I am using __int64
> but could of course use a more portable type.
>
> To perform the correct casts:
> double d = 3.3;
> __int64 i64;
> i64 = *((__int64*)&d);
>
> //And to convert back
> d = *((double*)&i64);
>
> But I don't understand how this works. Can someone please explain?

This will not work at all. You are reinterpreting memory
as a 64 bit integer, but you do NOT have a 64 bit
integer there!

A double has 3 parts: sign, exponent, and mantissa.

You will get a 64 bit integer with a value completely different
than the value stored in the double.

--
jacob navia
jacob at jacob point remcomp point fr
logiciels/informatique
http://www.cs.virginia.edu/~lcc-win32

Keith Thompson
Guest
Posts: n/a

 04-23-2008
Ben Bacarisse <(E-Mail Removed)> writes:
> Angus <(E-Mail Removed)> writes:
>
>> I need to convert a double to a 64 bit integer. I am using __int64
>> but could of course use a more portable type.
>>
>> To perform the correct casts:
>> double d = 3.3;
>> __int64 i64;
>> i64 = *((__int64*)&d);
>>
>> //And to convert back
>> d = *((double*)&i64);
>>
>> But I don't understand how this works. Can someone please explain?

>
> You convert a pointer to the double -- &d -- to an __int64 pointer
> -- (__int64 *)&d -- and then dereference that pointer to get the
> 64-bit integer that this converted pointer points to --
> *(__int64 *)&d.
>
> The code is not guaranteed to work (it is undefined behaviour) not
> least because the resulting integer might be a trap representation.

And because double and __int64 might have different alignment
constraints. Integer trap representations are fairly rare in the real
world; alignment constraints are common. Two types with the same size
are likely to have the same alignment constraints, though that's
certainly not guaranteed.

Note that if something like this causes a problem only rarely, the
conclusion to draw isn't necessarily that you can probably get away
with it; rather it means that it's a bug that can be very difficult to
detect.

> Such code is very common. Much more portable is:
>
> assert(sizeof d == sizeof i64);
> memcpy(&i64, &d, sizeof d);
>
> In fact the above is 100% portable, provided that one does not use the
> resulting integer (which is often enough).

Um, if you're not going to use the resulting integer, then what's the
point?

--
Keith Thompson (The_Other_Keith) <(E-Mail Removed)>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Richard Tobin
Guest
Posts: n/a

 04-23-2008
In article <funmbm\$8r5\$(E-Mail Removed)>, jacob navia <(E-Mail Removed)> wrote:

>> double d = 3.3;
>> __int64 i64;
>> i64 = *((__int64*)&d);
>>
>> //And to convert back
>> d = *((double*)&i64);

>This will not work at all. You are reinterpreting memory
>as a 64 bit integer, but you do NOT have a 64 bit
>integer there!
>
>A double has 3 parts: sign, exponent, and mantissa.
>
>You will get a 64 bit integer with a value completely different
>than the value stored in the double.

True, but that might be what he wants. If the idea is to get a 64-bit
integer with the same value as the double, it's certainly wrong. But
if he wants to inspect the bits of the double, then it's a plausible
way to do it.

Does this fall afoul of the type-based aliasing rules in C99?

-- Richard
--
:wq

muntyan@gmail.com
Guest
Posts: n/a

 04-23-2008
On Apr 23, 10:04 am, Angus <(E-Mail Removed)> wrote:
> I need to convert a double to a 64 bit integer. I am using __int64
> but could of course use a more portable type.
>
> To perform the correct casts:
> double d = 3.3;
> __int64 i64;
> i64 = *((__int64*)&d);
>
> //And to convert back
> d = *((double*)&i64);
>
> But I don't understand how this works. Can someone please explain?

This code is broken and will or will not work depending on the
compiler and its settings, e.g. it won't work with gcc -O2. Use a
union to do that:

union {
double d;
__int64 i;
} u;
u.d = 3.3;
// use u.i now
// take u.d to "convert" u.i back to double

(If you are only going to compile your code with casts with
MS compiler, then it's probably fine as it is, it's unlikely
MS will break such code. But the code is broken nevertheless)

Yevgen

Keith Thompson
Guest
Posts: n/a

 04-23-2008
jacob navia <(E-Mail Removed)> writes:
> Angus wrote:
>> I need to convert a double to a 64 bit integer. I am using __int64
>> but could of course use a more portable type.
>>
>> To perform the correct casts:
>> double d = 3.3;
>> __int64 i64;
>> i64 = *((__int64*)&d);
>>
>> //And to convert back
>> d = *((double*)&i64);
>>
>> But I don't understand how this works. Can someone please explain?

>
> This will not work at all. You are reinterpreting memory
> as a 64 bit integer, but you do NOT have a 64 bit
> integer there!
>
> A double has 3 parts: sign, exponent, and mantissa.
>
> You will get a 64 bit integer with a value completely different
> than the value stored in the double.

Which *might* be exactly what he wants. In my earlier followup, I
assumed that Angus actually wanted to obtain the representation of the
double value as a 64-bit integer. jacob is correct that this might
not make much sense; on the other hand, Angus hasn't given us enough
information to be sure about what he's trying to accomplish.

If you want to obtain the representation, using memcpy() as Ben
Bacarisse suggested is probably the best approach, though it can be
perilous.

If you want a *value* conversion, just assign it (the result will be
3, and you'll lose the .3 fractional part). (A cast will perform an
explicit value conversion, but it's usually not needed; in most
contexts, any needed conversion will be done implicitly.)

For example:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int main(void)
{
double d = 3.3;
unsigned long long rep;
unsigned long long val;
assert(sizeof d == sizeof rep);
memcpy(&rep, &d, sizeof d);
val = d;
printf("d = %g\n", d);
printf("rep = 0x%llx\n", rep);
printf("val = %lld\n", val);
return 0;
}

On my system, I get:

d = 3.3
rep = 0x400a666666666666
val = 3

--
Keith Thompson (The_Other_Keith) <(E-Mail Removed)>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Angus
Guest
Posts: n/a

 04-23-2008
On Apr 23, 5:29*pm, Keith Thompson <(E-Mail Removed)> wrote:
> jacob navia <(E-Mail Removed)> writes:
> > Angus wrote:
> >> I need to convert a double to a 64 bit integer. *I am using __int64
> >> but could of course use a more portable type.

>
> >> To perform the correct casts:
> >> * * double d = 3.3;
> >> * * __int64 i64;
> >> * * i64 = *((__int64*)&d);

>
> >> * * //And to convert back
> >> * * d = *((double*)&i64);

>
> >> But I don't understand how this works. *Can someone please explain?

>
> > This will not work at all. You are reinterpreting memory
> > as a 64 bit integer, but you do NOT have a 64 bit
> > integer there!

>
> > A double has 3 parts: sign, exponent, and mantissa.

>
> > You will get a 64 bit integer with a value completely different
> > than the value stored in the double.

>
> Which *might* be exactly what he wants. *In my earlier followup, I
> assumed that Angus actually wanted to obtain the representation of the
> double value as a 64-bit integer. *jacob is correct that this might
> not make much sense; on the other hand, Angus hasn't given us enough
> information to be sure about what he's trying to accomplish.
>
> If you want to obtain the representation, using memcpy() as Ben
> Bacarisse suggested is probably the best approach, though it can be
> perilous.
>
> If you want a *value* conversion, just assign it (the result will be
> 3, and you'll lose the .3 fractional part). *(A cast will perform an
> explicit value conversion, but it's usually not needed; in most
> contexts, any needed conversion will be done implicitly.)
>
> For example:
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <assert.h>
> int main(void)
> {
> * * double d = 3.3;
> * * unsigned long long rep;
> * * unsigned long long val;
> * * assert(sizeof d == sizeof rep);
> * * memcpy(&rep, &d, sizeof d);
> * * val = d;
> * * printf("d = %g\n", d);
> * * printf("rep = 0x%llx\n", rep);
> * * printf("val = %lld\n", val);
> * * return 0;
>
> }
>
> On my system, I get:
>
> d = 3.3
> rep = 0x400a666666666666
> val = 3
>
> --
> Keith Thompson (The_Other_Keith) <(E-Mail Removed)>
> Nokia
> "We must do something. *This is something. *Therefore, we must do this.."
> * * -- Antony Jay and Jonathan Lynn, "Yes Minister"- Hide quoted text -
>
> - Show quoted text -

The reason for doing this is to transfer a double value across a
network using sockets. As I cannot think of a way to transport a
double value but I can send a 64 bit integer as a sequence of bytes
then this would work. Maybe I should be doing this totally some other
way.

As present I use an MS compiler for both ends of the connection so
that is not a problem. But maybe in the future that could change.

I was concerned that the memcpy way to do this might works but not if
I try to use the resulting integer! Well, that is not so useful...
Why can I not access the resulting integer?

Should I be doing this some other way?

Angus
Guest
Posts: n/a

 04-23-2008
On Apr 23, 5:37*pm, Angus <(E-Mail Removed)> wrote:
> On Apr 23, 5:29*pm, Keith Thompson <(E-Mail Removed)> wrote:
>
>
>
>
>
> > jacob navia <(E-Mail Removed)> writes:
> > > Angus wrote:
> > >> I need to convert a double to a 64 bit integer. *I am using __int64
> > >> but could of course use a more portable type.

>
> > >> To perform the correct casts:
> > >> * * double d = 3.3;
> > >> * * __int64 i64;
> > >> * * i64 = *((__int64*)&d);

>
> > >> * * //And to convert back
> > >> * * d = *((double*)&i64);

>
> > >> But I don't understand how this works. *Can someone please explain?

>
> > > This will not work at all. You are reinterpreting memory
> > > as a 64 bit integer, but you do NOT have a 64 bit
> > > integer there!

>
> > > A double has 3 parts: sign, exponent, and mantissa.

>
> > > You will get a 64 bit integer with a value completely different
> > > than the value stored in the double.

>
> > Which *might* be exactly what he wants. *In my earlier followup, I
> > assumed that Angus actually wanted to obtain the representation of the
> > double value as a 64-bit integer. *jacob is correct that this might
> > not make much sense; on the other hand, Angus hasn't given us enough
> > information to be sure about what he's trying to accomplish.

>
> > If you want to obtain the representation, using memcpy() as Ben
> > Bacarisse suggested is probably the best approach, though it can be
> > perilous.

>
> > If you want a *value* conversion, just assign it (the result will be
> > 3, and you'll lose the .3 fractional part). *(A cast will perform an
> > explicit value conversion, but it's usually not needed; in most
> > contexts, any needed conversion will be done implicitly.)

>
> > For example:

>
> > #include <stdio.h>
> > #include <stdlib.h>
> > #include <assert.h>
> > int main(void)
> > {
> > * * double d = 3.3;
> > * * unsigned long long rep;
> > * * unsigned long long val;
> > * * assert(sizeof d == sizeof rep);
> > * * memcpy(&rep, &d, sizeof d);
> > * * val = d;
> > * * printf("d = %g\n", d);
> > * * printf("rep = 0x%llx\n", rep);
> > * * printf("val = %lld\n", val);
> > * * return 0;

>
> > }

>
> > On my system, I get:

>
> > d = 3.3
> > rep = 0x400a666666666666
> > val = 3

>
> > --
> > Keith Thompson (The_Other_Keith) <(E-Mail Removed)>
> > Nokia
> > "We must do something. *This is something. *Therefore, we must do this."
> > * * -- Antony Jay and Jonathan Lynn, "Yes Minister"- Hide quoted text -

>
> > - Show quoted text -

>
> The reason for doing this is to transfer a double value across a
> network using sockets. *As I cannot think of a way to transport a
> double value but I can send a 64 bit integer as a sequence of bytes
> then this would work. *Maybe I should be doing this totally some other
> way.
>
> As present I use an MS compiler for both ends of the connection so
> that is not a problem. *But maybe in the future that could change.
>
> I was concerned that the memcpy way to do this might works but not if
> I try to use the resulting integer! *Well, that is not so useful...
> Why can I not access the resulting integer?
>
> Should I be doing this some other way?- Hide quoted text -
>
> - Show quoted text -

Just thought I should also add that I definitely need the fractional
floating part.

Ben Bacarisse
Guest
Posts: n/a

 04-23-2008
Keith Thompson <(E-Mail Removed)> writes:
<snip>
> Ben Bacarisse <(E-Mail Removed)> writes:
>> Such code is very common. Much more portable is:
>>
>> assert(sizeof d == sizeof i64);
>> memcpy(&i64, &d, sizeof d);
>>
>> In fact the above is 100% portable, provided that one does not use the
>> resulting integer (which is often enough).

>
> Um, if you're not going to use the resulting integer, then what's the
> point?

Given a hash table that stores 6-bit ints keyed by, say, strings the
hack above could be used to press-gang that code into storing doubles.
This will remain portable (though ugly) if the hash table returns a
pointer to its value (often done, of course, because that gives
natural return value for "key not present"). It is not uncommon to
see hackery like this when one data structure is abused to store
values of some other type, and the results can be portable if the
values are never referenced until they are converted back.

If you meant the actual code above is useless, I agree, but it was an
illustration of an idea, not a working example.

--
Ben.