470,648 Members | 1,608 Online

# Conversion between float and long

I was very puzzled about the conversion between float and long, I
cann't understand why a long val can convert to a float, as the below
codes show:

typedef unsigned long u_long;
float val = 3.14159;
u_long nw_val = *((u_long *) &val);

than the nw_val equal to 1078530000, I made such conversion:
float d_val = *((float*)&nw_val);

than I got d_val = 3.14159

Can anybody help me explains this sentence *((u_long*) &val) ?

best regards!

Jun 27 '08 #1
8 13521
d major <ji*****@gmail.comwrites:
>I was very puzzled about the conversion between float and long,
http://www-h.eng.cam.ac.uk/help/tpl/...ongtyping.html
might help.

>Can anybody help me explains this sentence *((u_long*) &val) ?
You're creating a pointer of type u_long* and pointing it at a place
where a float has been stored. When you dereference this pointer (using
the first of the "*" symbols) the bit-pattern representation of the
float's value is being treated as if it were the representation of a u_long.
floats and ints are stored in different formats, so you get a strange value.

Jun 27 '08 #2
On 2008-05-21 06:37:04 -0400, d major <ji*****@gmail.comsaid:
I was very puzzled about the conversion between float and long, I
cann't understand why a long val can convert to a float, as the below
codes show:

typedef unsigned long u_long;
float val = 3.14159;
u_long nw_val = *((u_long *) &val);

than the nw_val equal to 1078530000, I made such conversion:
float d_val = *((float*)&nw_val);

than I got d_val = 3.14159

Can anybody help me explains this sentence *((u_long*) &val) ?
Please don't call this a conversion between float and long. It's not.
It's a conversion between pointers, and the cast should be a hint that
someting isn't kosher. In particular, if an unsigned long and a float
are different sizes, at worst you'll get a nonsensical result, and at
best you'll get a hardware trap. If they're the same size (which is
unusual these days), the conversion will probably work, and then the
unsigned long that you get from dereferencing the converted pointer
will have the same bit pattern as the float. But of course the meaning
of those bits is completely different. This is low-level hacker stuff;
don't do it.

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

Jun 27 '08 #3
On May 21, 1:00*pm, t...@eng.cam.ac.uk (Tim Love) wrote:
d major <jing...@gmail.comwrites:
I was very puzzled about the conversion between float and long,

*http://www-h.eng.cam.ac.uk/help/tpl/...ongtyping.html
might help.
Can anybody help me explains this sentence *((u_long*) &val) ?

You're creating a pointer of type u_long* and pointing it at a place
where a float has been stored. When you dereference this pointer (using
the first of the "*" symbols) the bit-pattern representation of the
float's value is being treated as if it were the representation of a u_long.
floats and ints are stored in different formats, so you get a strange value.
And, BTW, reading from a memory location using a type different than
its dynamic
type is undefined behavior as it breaks strict aliasing, and will
actually
break on real modern compilers.

Note that the union trick, explained else post, is also undefined but
it is a common
extension to the language.

The correct way to implement this type of type punning is using
std::memcpy.

HTH,

--
Giovanni P. Deretta
Jun 27 '08 #4
Hi!

d major schrieb:
I was very puzzled about the conversion between float and long, I
cann't understand why a long val can convert to a float,
It can. And apart from the discussion about the *pointer* cast I want to
show the simple numeric cast:

#include <iostream>
#include <ostream>
int main()
{
const float f = 3.1415962f;
const long l = f;
const float f2 = l;
std::cout << f2 << std::endl;
}

Which simply prints "3". "l" is created from "f" which is a float. "l =
f" is a numeric conversion and will likely produce a compiler warning.
"f2 = l" is another conversion in the reverse direction: from long to
float. It does not produce a warning as it is a standard "promotion" (I
think): the range of a float is assumed to be larger than the range of a
long, so there shouldn't be a problem (hehe, loss of precision probably,
but anyway).

Frank
Jun 27 '08 #5
On 21 mai, 17:26, gpderetta <gpdere...@gmail.comwrote:
On May 21, 1:00 pm, t...@eng.cam.ac.uk (Tim Love) wrote:
d major <jing...@gmail.comwrites:
>I was very puzzled about the conversion between float and long,
http://www-h.eng.cam.ac.uk/help/tpl/...ongtyping.html
might help.
>Can anybody help me explains this sentence *((u_long*) &val) ?
You're creating a pointer of type u_long* and pointing it at
a place where a float has been stored. When you dereference
this pointer (using the first of the "*" symbols) the
bit-pattern representation of the float's value is being
treated as if it were the representation of a u_long.
floats and ints are stored in different formats, so you get
a strange value.
And, BTW, reading from a memory location using a type
different than its dynamic type is undefined behavior as it
breaks strict aliasing, and will actually break on real modern
compilers.
It's well defined if the target type is a character pointer;
you're allowed to read the raw bytes of an "object". It's also
fairly clearly the intent of the standard that it should work
more or less as expected for other basic types.

It also happens that making it work wrecks havoc with the
optimizer, and can slow code down considerably, so compilers
don't normally do it unless the casts are very local and very
visible. (A good compiler will turn off optimizing if it sees a
reinterpret_cast in a block. That still won't help if you pass
the converted pointer to another function, however.)
Note that the union trick, explained else post, is also
undefined but it is a common extension to the language.
The advantage of the union trick (from a compiler author's point
of view) is that the aliasing is immediately visible. But it
doesn't necessarily work either if you take the address of each
of the members, and pass those addresses to another function.
The correct way to implement this type of type punning is
using std::memcpy.
The more correct thing to do is not to implement it at all:-).

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #6
On May 21, 10:20*pm, James Kanze <james.ka...@gmail.comwrote:
On 21 mai, 17:26, gpderetta <gpdere...@gmail.comwrote:
On May 21, 1:00 pm, t...@eng.cam.ac.uk (Tim Love) wrote:
d major <jing...@gmail.comwrites:
I was very puzzled about the conversion between float and long,
*http://www-h.eng.cam.ac.uk/help/tpl/...ongtyping.html
might help.
Can anybody help me explains this sentence *((u_long*) &val) ?
You're creating a pointer of type u_long* and pointing it at
a place where a float has been stored. When you dereference
this pointer (using the first of the "*" symbols) the
bit-pattern representation of the float's value is being
treated as if it were the representation of a u_long.
floats and ints are stored in different formats, so you get
a strange value.
And, BTW, reading from a memory location using a type
different than its dynamic type is undefined behavior as it
breaks strict aliasing, and will actually break on real modern
compilers.

It's well defined if the target type is a character pointer;
you're allowed to read the raw bytes of an "object". *
Yes, forgot to mention that.
It's also
fairly clearly the intent of the standard that it should work
more or less as expected for other basic types.
I'm not convinced about that. In fact I have read experts discussing
this that made it clear that strict aliasing also applies to all
types
(modulo exceptions like chars). In particular the guaranteed freedom
of
aliasing between integral and floating point types could very well
speed
up real numeric code (which uses integers for indexes and floats for
computation). Or at least, this is what I've read.

Type aliasing is still a very controversial topic, as you can see
by browsing gcc bugzilla :)

There are also a couple of open issues on the C standard regarding
this
topic.
[...]
The correct way to implement this type of type punning is
using std::memcpy.

The more correct thing to do is not to implement it at all:-).
;)

Of course, but in real life it is sometime necessary for some system
specific operations...

... or optimizations *ducks*.

--
Giovanni P. Deretta
Jun 27 '08 #7
On May 21, 11:12*pm, Michael DOUBEZ <michael.dou...@free.frwrote:
gpderetta a écrit :
And, BTW, reading from a memory location using a type different than
its dynamic
type is undefined behavior as it breaks strict aliasing,

As soon as you have a reinterpret_cast<>(), you have however UB.
I think that it is actually implementation defined.

For example the POSIX standard practically requires is it do deal
with
and will actually break on real modern compilers.

IIRC on gcc, strict aliasing is only activated from -02 optimisation
In current releases, in future it could been enabled even at lower
optimization levels...
and
you can always pass a -fno-strict-aliasing to avoid the optimisation.
... and, as the man page says, -fno-strict-aliasing is not guaranteed
to be supported in the future. I would be surprised if it were to be
dropped though, way too much software would break.
>
Does it really break when only reading values or passing a pointer
around (float*->long*->float*) ? I though aliasing was a problem only
upon writing (i.e. the value is not propagated to heterogeneous readers).
I do not know, it might or might not. I think the gcc developers have
explicitly refused to guarantee it.

--
Giovanni P. Deretta
Jun 27 '08 #8
On May 22, 12:30 am, gpderetta <gpdere...@gmail.comwrote:
On May 21, 10:20 pm, James Kanze <james.ka...@gmail.comwrote:
On 21 mai, 17:26, gpderetta <gpdere...@gmail.comwrote:
On May 21, 1:00 pm, t...@eng.cam.ac.uk (Tim Love) wrote:
d major <jing...@gmail.comwrites:
>I was very puzzled about the conversion between float and long,
http://www-h.eng.cam.ac.uk/help/tpl/...ongtyping.html
might help.
>Can anybody help me explains this sentence *((u_long*) &val) ?
You're creating a pointer of type u_long* and pointing it at
a place where a float has been stored. When you dereference
this pointer (using the first of the "*" symbols) the
bit-pattern representation of the float's value is being
treated as if it were the representation of a u_long.
floats and ints are stored in different formats, so you get
a strange value.
And, BTW, reading from a memory location using a type
different than its dynamic type is undefined behavior as it
breaks strict aliasing, and will actually break on real modern
compilers.
It's well defined if the target type is a character pointer;
you're allowed to read the raw bytes of an "object".
Yes, forgot to mention that.
Note that we're talking about the standard here. Any
relationship between what the standard requires and what any
particular implementation does is purely coincidental.
It's also fairly clearly the intent of the standard that it
should work more or less as expected for other basic types.
The note in §5.2.10/4, concerning the mapping done by
reinterpret_cast: "it is intended to be unsurprising to those
who know the addressing structure of the underlying machine."
Strictly speaking, this note concerns the mapping between
integers and pointer types, but it seems reasonable to expect it
to further apply between two pointer types.
In fact I have read experts discussing this that made it clear
that strict aliasing also applies to all types (modulo
exceptions like chars). In particular the guaranteed freedom
of aliasing between integral and floating point types could
very well speed up real numeric code (which uses integers for
indexes and floats for computation). Or at least, this is what
It's a case of the left hand not knowing what the right hand is
doing:-). The rules concerning aliasing are very important for
optimizing, and C++ very clearly does say in its object model
that accessing an object via an lvalue of a different type
(other than a character type) is undefined behavior. Regardless
of how you do it. On the other hand, reinterpret_cast is
useless unless you can do it. Clearly, reinterpret_cast is not
meant for portable code, but arguably, it should be usable in an
implementation defined manner. And the "undefined behavior" in
the object model is not because of optimizing, but because
accessing an int as a double (for example) might result in a
trapping representation. (But we don't have a rationale for the
C++ standard, so we don't know the real motivations.)

Practically, from a quality of implementation point of view, I'd
expect such accesses to behave in a manner "unsurprising to
those who know the addressing structure of the underlying
machine", and the exact representations of the types involved,
if, but only if, the reinterpret_cast is clearly visible to the
compiler, i.e. if I write something like:

void
f( double* d )
{
unsigned long long* p
= reinterpret_cast< unsigned long long* >( d ) ;
*p = 0x4000000000000000ULL ;
}

I expect the double pointed to by d to be modified to contain
the specified bit pattern, and that code calling this function,
say:

void
g()
{
double d = 0.0 ;
f( &d ) ;
std::cout << d << std::endl ;
}

will output the expected value (1.0, if I'm not mistaken with my
integral literal). Either the compiler knows what is in f()
(e.g. because it is inline), can see the reinterpret_cast, and
so knows that strict aliasing no longer applies, or it doesn't
know, in which case, it has to assume that f modifies d, and
thus reread the value before calling operator<<.

I don't expect it to work in a function which gets the two
pointers (one double*, one unsigned long long*) from some
external, unknown source.
Type aliasing is still a very controversial topic, as you can
see by browsing gcc bugzilla :)
It's essential for good optimization. All that one can
reasonably ask is that the compiler drop it when it sees a
reinterpret_cast.
There are also a couple of open issues on the C standard
regarding this topic.
I can believe it.

My understanding of the intent in C90 was that casting, and not
unions should be used for type punning. Admittedly, however,
this is based on somewhat uncertain memories of vague
discussions many years ago, so I'm not sure how reliable it is.
Still, it seems clear to me that a union introduces still an

union U
{
double d1 ;
double d2 ;
} u ;

Formally, if you write to u.d1, and read from u.d2, you have
undefined behavior. Supposedly, in theory at least, an
implementation could keep a tag cached somewhere hidden, and
check it when you accessed. (How such an implementation would
deal with something like *(double*)(&u), I don't know, since
I think there is a guarantee that casting the address of a union
to the type of an address to one its members results in a
pointer to the member.)

Practically (again from a quality of implemenation point of
view), I'd expect type punning with a union to work as long as
the accesses are all directly to the union---again, if you take
the address of two members of different types, and pass them to
another function, I think that that function has the right to
suppose that different types means non-overlapping objects. But
I've used at least one C compiler where this was NOT the case.
[...]
The correct way to implement this type of type punning is
using std::memcpy.
The more correct thing to do is not to implement it at all:-).
;)
Of course, but in real life it is sometime necessary for some
system specific operations...
If you're writing very low level software, you almost have to.
How would you write a garbage collector without breaking typing,
for example?
... or optimizations *ducks*.
No need to duck. As I've said more than once, if the profiler
says you have to, you have to.

You generally have an engineering decision: you don't need the
type punning, but if it can be done reasonably safely, and saves
a couple of days of development time...

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #9

### This discussion thread is closed

Replies have been disabled for this discussion.