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

A

Angus

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?
 
B

Ben Bacarisse

Angus said:
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).
 
J

jacob navia

Angus said:
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.
 
K

Keith Thompson

Ben Bacarisse said:
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?
 
R

Richard Tobin

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
 
M

muntyan

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
 
K

Keith Thompson

jacob navia said:
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
 
A

Angus

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) <[email protected]>
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?
 
A

Angus

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.
 
B

Ben Bacarisse

Keith Thompson said:
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.
 
B

Ben Bacarisse

Angus said:
On Apr 23, 5:29 pm, Keith Thompson <[email protected]> wrote:
If you want to obtain the representation, using memcpy() as Ben
Bacarisse suggested is probably the best approach, though it can be
perilous.

Best not to quote signature blocks (the -- bit).

The reason for doing this is to transfer a double value across a
network using sockets.

Ah. Much better just to send the bytes. C guarantees that all
objects can be accessed as unsigned char arrays. The payload of your
data packets is, usually, a sequence of unsigned chars, so just use:

memcpy(pkt_ptr, &d, sizeof d);
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.

If you need a method that works between different systems, the most
portable would be to convert the double to printable decimal, though
this can have consequences for converting back (you won't always get
the same double you started with).
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?

Because it might be a trap representation. This won't be a problem on
today's boring architectures. Keith Thompson's other concern, that
the data might not be correctly aligned, won't be an issue if the
destination is a declared 64-bit int (say a variable or a field in a
struct). It *will* be a concern if you pull an 64-bit int out of an
arbitrary location in a message buffer:

long long int i =
*(long long int *)pkt_ptr; /* Eek! Anything could happen. */
 
K

Keith Thompson

Ben Bacarisse said:
Because it might be a trap representation. This won't be a problem on
today's boring architectures. Keith Thompson's other concern, that
the data might not be correctly aligned, won't be an issue if the
destination is a declared 64-bit int (say a variable or a field in a
struct). It *will* be a concern if you pull an 64-bit int out of an
arbitrary location in a message buffer:

long long int i =
*(long long int *)pkt_ptr; /* Eek! Anything could happen. */

The original code was:

double d = 3.3;
__int64 i64;
i64 = *((__int64*)&d);

&d is appropriately aligned for type double. If __int64 requires
stricter alignment than double does, this could cause undefined
behavior. I wouldn't be surprised if this particular problem never
actually occurs, but the standard doesn't guarantee that it will work.

Using a union solves the alignment problem, but still invokes UB
because the standard specifically says so.

For transmitting the information across a network, the best solution
is probably just to treat the double value as an array of the
sizeof(double) unsigned chars, and copy it to a double object on the
receiving end. This assumes both ends use the same representation for
double.

If you can't make that assumption, you can transmit a textual
representation of the value, with enough digits to avoid losing
information.
 
H

Harald van Dijk

The original code was:

double d = 3.3;
__int64 i64;
i64 = *((__int64*)&d);

&d is appropriately aligned for type double. If __int64 requires
stricter alignment than double does, this could cause undefined
behavior. I wouldn't be surprised if this particular problem never
actually occurs, but the standard doesn't guarantee that it will work.

Using a union solves the alignment problem, but still invokes UB because
the standard specifically says so.

Nope, it doesn't. In C90, IIRC, reading a value from a union other than
the one last stored causes implementation-defined behaviour, but C99
requires the intended behaviour, which is a reinterpretation of the bit
pattern.
 
M

muntyan

The original code was:

double d = 3.3;
__int64 i64;
i64 = *((__int64*)&d);

&d is appropriately aligned for type double. If __int64 requires
stricter alignment than double does, this could cause undefined
behavior. I wouldn't be surprised if this particular problem never
actually occurs, but the standard doesn't guarantee that it will work.

Using a union solves the alignment problem,

As well as the actual problem of getting wrong results
on existing boring systems.
but still invokes UB
because the standard specifically says so.

But works in practice and will likely continue to work.

Yevgen
 
K

Keith Thompson

Harald van Dijk said:
Nope, it doesn't. In C90, IIRC, reading a value from a union other than
the one last stored causes implementation-defined behaviour, but C99
requires the intended behaviour, which is a reinterpretation of the bit
pattern.

You're right. A footnote in C99 6.5.2.3 says:

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 as
described in 6.2.6 (a process sometimes called "type
punning"). This might be a trap representation.

Note that a trap representation is a possibility in this case (though
most implementations don't have trap representations for integral
types).
 
N

Nick Keighley

The original code was:

     double d = 3.3;
     __int64 i64;
     i64 = *((__int64*)&d);

the OP is doing this to send a double over a notwork

For transmitting the information across a network, the best solution
is probably just to treat the double value as an array of the
sizeof(double) unsigned chars, and copy it to a double object on the
receiving end.  This assumes both ends use the same representation for
double.

If you can't make that assumption, you can transmit a textual
representation of the value, with enough digits to avoid losing
information.

eg. sprintf()
or ASN.1
or XDR

there are hex representations of floats as well
 
Y

ymuntyan

You're right.

That is UB, because of aliasing rules (of course it will do
something harmful only on a death station). And non-active
union member access wasn't UB in C90.
A footnote in C99 6.5.2.3 says:

[snip]

Note that a trap representation is a possibility in this case (though
most implementations don't have trap representations for integral
types).

Note that the size mismatch is a possibility too. If you
do that type punning stuff, then you are probably aware
of the type characteristics, no? Funny stuff: when will
given implementation-specific code invoke UB?

Yevgen
 
D

David Thompson

You're right. A footnote in C99 6.5.2.3 says: <snip>

Only after (alleged!) correction by TC3-2007. FWTW.

- formerly david.thompson1 || achar(64) || worldnet.att.net
 
H

Harald van Dijk

Only after (alleged!) correction by TC3-2007. FWTW.

The footnote only makes explicit what the original C99 standard already
required. C99 requires this behaviour by making sure the aliasing rules do
not disallow it and removing the wording saying the result is
implementation-defined. Please note that TC3 has made no relevant changes
in the normative text about this.
 
S

Stephen Sprunk

Angus said:
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. ....
Should I be doing this some other way?

If you already understand how to send a 64-bit int over your socket by
reading it as an array of bytes, then just do the exact same thing with a
64-bit double; there is no need to stuff the double into an int first.

If you are using similar systems on either end, everything will be fine. If
not, you've got all sorts of problems with endianness, floating point
representation, variable sizes, etc. that may/will appear. Odds are,
though, that many of those problems will appear with your code to transfer
ints as well when you port it to a new system.

The safest and most portable way to transfer data between systems is to
convert it to a textual representation -- but FP types may lose a tiny bit
of accuracy during the conversion process. Any attempt at moving binary
data requires a lot of attention to specifying the wire format and code to
convert to/from that wire format on each machine, which has its own problems
but is typically faster.
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?

Your double may be a trap representation when interpreted as an int. Using
memcpy() or a union eliminates the far more common problem of incorrect
alignment, though.

S
 

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

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top