Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Int -> Float

Credits go to tobing for helping out!
This thread is locked; no one can reply to it. rss feed Print
Int -> Float
Damian Grove
Member #6,758
January 2006

I'm not sure how to do this. I have ints, and one of them I want to read as a float with the same exactly hexadecimal value. In other words if the integer is 0x3F000000, I want the float value to also be 0x3F000000 (or 0.5 in decimal format). I haven't found a way to do this. Obviously casting alone doesn't work, so I'm stuck.

Get into awesome with Saxman!

tobing
Member #5,213
November 2004
avatar

You could have two variables, one int and one float, then memcpy 4 bytes from one to the other.

Or make a union, consisting of an int and a float, use one to assign, then the other to access the contents.

Damian Grove
Member #6,758
January 2006

I've never heard of memcpy. But I did a quick search for it on Google and found some documentation on how to use it.

Thanks for the tip!

Get into awesome with Saxman!

Billybob
Member #3,136
January 2003

int x = 0x3F000000;
float y = *((float *)&x);

It does literally what you want. It reads x as a float.
&x : Pointer to x
(float *) : Convert the int pointer into a float pointer
*(...) : Resolve the pointer to a value.

Carrus85
Member #2,633
August 2002
avatar

C Unions will also do this as well:

union Multitype {
    int int_mode;
    float float_mode;
};

// inside of your code

union Multitype myType;
myType.int_mode = 0x3F000000;
float myFloat = myType.float_mode;

orz
Member #565
August 2000

In C++: (well, some would use reinterpret_cast<>, but I do this)
float f; (int&)f = 0x3F000000;
In C:
float f; *((int*)&f) = 0x3F000000;

Goalie Ca
Member #2,579
July 2002
avatar

My vote goes to reinterpret casts but make sure that all the bits for the sign, mantissa, etc. line up.

http://en.wikipedia.org/wiki/IEEE_754

-------------
Bah weep granah weep nini bong!

Thomas Harte
Member #33
April 2000
avatar

Quote:

My vote goes to reinterpret casts but make sure that all the bits for the sign, mantissa, etc. line up.

Yeah, the safest way to do this is to decode your integer into sign, mantissa and exponent then load them into a floating point number by just casting (the regular C kind) the integer to float then using ldexp to fix up the exponent and negativing the whole thing if you need. But I'm sure you already have a solution you're happy with...

Andrei Ellman
Member #3,434
April 2003

If you are writing the ints to a file or sending down a network, don't forget to take into account the differences between big-endian and little-endian machines when designing a file-format.

AE.

--
Don't let the illegitimates turn you into carbon.

Bob
Free Market Evangelist
September 2000
avatar

You do not want to use casting (including reinterpret casts). The compiler is free to optimize away both sides of the statement by removing all relevant code.

You should use a union instead.

Moreover, you'll want to wrap that union in a function, so that if unions don't work on one platform, there's just one function to fix later on.

Finally, you'll want to pass all floats by reference, to avoid canonicalizing NaN (if you care about those).

--
- Bob
[ -- All my signature links are 404 -- ]

orz
Member #565
August 2000

Quote:

You do not want to use casting (including reinterpret casts). The compiler is free to optimize away both sides of the statement by removing all relevant code.

You should use a union instead.

What? Explain. So far as I can tell, none of the code posted in this thread is in any particular danger of not accomplishing its purpose due to agresive optimizers. It might not do exactly what it's supposed to do if floats aren't IEEE format 32 bit floats or ints aren't 32 bits, but that's also true of unions, and has little or no relationship to the optimizer. Indeed, I can not think of any reason why the union based version would have any advantage unless ints were larger than floats on the target platform (I don't think such a platform exists, though I believe the spec allows it, but that's independent of optimizer anyway).

Quote:

Moreover, you'll want to wrap that union in a function, so that if unions don't work on one platform, there's just one function to fix later on.

This makes perfect sense to me.

Quote:

Finally, you'll want to pass all floats by reference, to avoid canonicalizing NaN (if you care about those).

This does not immediately make sense to me, though it calls to mind vague memories with obscure NaN issues. Googling for "canonicalizing NaN" found no results. I vaguely recall x86s changing some NaNs to other NaNs (ie still NaN but not binary identical to the previous state) if they aren't passed by reference (or conceivably even if they are, if the compiler is feeling obnoxious)... is that what you're referring to?

Bob
Free Market Evangelist
September 2000
avatar

Quote:

So far as I can tell, none of the code posted in this thread is in any particular danger of not accomplishing its purpose due to agresive optimizers.

That's a very dangerous platform-dependent thought you're having here :)

Quote:

What? Explain.

Just read up the GCC manual:

Quote:

-fstrict-aliasing
Allows the compiler to assume the strictest aliasing rules applicable to the language being compiled. For C (and C++), this activates optimizations based on the type of expressions. In particular, an object of one type is assumed never to reside at the same address as an object of a different type, unless the types are almost the same. For example, an unsigned int can alias an int, but not a void* or a double. A character type may alias any other type.

Pay special attention to code like this:
union a_union {
int i;
double d;
};

int f() {
a_union t;
t.d = 3.0;
return t.i;
}

The practice of reading from a different union member than the one most recently written to (called “type-punning”) is common. Even with -fstrict-aliasing, type-punning is allowed, provided the memory is accessed through the union type. So, the code above will work as expected. However, this code might not:
int f() {
a_union t;
int* ip;
t.d = 3.0;
ip = &t.i;
return *ip;
}

Enabled at levels -O2, -O3, -Os.

(Emphasis mine)
Linky

Quote:

is that what you're referring to?

Yes. In general, you don't care about the NaN form you are using. But when needing to send particular bit patterns for testing some code, you might care about the exact encoding of those NaNs...

--
- Bob
[ -- All my signature links are 404 -- ]

orz
Member #565
August 2000

Quote:

That's a very dangerous platform-dependent tought you're having here

I usually don't mind platform dependence when the "platform" in question includes the compilers of MS, GNU, and Intel, and the instruction sets of x86, x86-64, PPC, and most other modern general purpose CPUs (edit: and the OSes of windows and linux and OS X). But, in this particular case I added the qualifier of "due to agresive optimizers" to specify that I wasn't counting circumstances where the code failed due to other reasons like lack of IEEE compliant floating point representation.

Quote:

Just read up the GCC manual:
Quote:
...

Hm... yeah, I guess the stuff added in C99 allows pointer casts to optimize weirdly. Presumably recent versions of the C++ spec have something similar permitting the the compiler to be compliant while optimizing reference casts weirdly. I believe that does not apply to reinterpret_cast<> versions. Though, by a worse-case literal reading of the standard, I guess the implementation of reinterpret_cast<> is basically undefined.

However, despite you being correct, I don't really agree : ). Just as I use "kilobyte" to refer to 1024 bytes instead of 1000 bytes that some standards body has defined it to mean (the standards body decided that 1024 bytes is a "kibibyte" instead), so do I interpret "C" and "C++" to be more or less the common elements of C/C++ compilers (particularly recent gcc, MSVC, and Intel-C versions, particularly for IA16, IA32, x86-64, PPC, and ARM) and my expectations of what those common elements might be in the future. The C/C++ standard permits compilers to assume that that incompatible pointer types point at different memory, but I operate under the assumption that this only applies if it is non-trivial for the compiler to figure out that the pointers are identical at compile time (often because of a function call boundary between the cast and the dereference, though plenty of other reasons apply too).
(now watch someone compile the above snippets on some gcc version I don't have installed and get nonsense results, proving me wrong)

Quote:

Yes. In general, you don't care about the NaN form you are using. But when needing to send particular bit patterns for testing some code, you might care about the exact encoding of those NaNs...

Ah, well, now I know what you were saying. Dunno how many people around here are likely to understand "canonicalizing NaN", but there's probably someone besides me who scratched his head.

Bob
Free Market Evangelist
September 2000
avatar

Quote:

However, despite you being correct, I don't really agree : )

This isn't an opinion issue. Some versions of GCC will optimize away some (supposedly dead) code when you'd think it wouldn't.

--
- Bob
[ -- All my signature links are 404 -- ]

Wetimer
Member #1,622
November 2001

Umm...

Why? Why would you want to do that?

<code>if(Windows.State = Crash) Computer.halt();</code>

orz
Member #565
August 2000

Quote:

This isn't an opinion issue. Some versions of GCC will optimize away some (supposedly dead) code when you'd think it wouldn't.

All gcc (and MSVC versions) I've used will optimize away supposedly dead code. None I've used will optimize away the above snippets just because of aliasing rules. They may get optimized away if the results are never used, but that's a different issue entirely (and effects the union version just as much as the pointer/reference casting or the reinterpret_cast<>ing version).

Quote:

Umm...

Why? Why would you want to do that?

What, put a known binary value into a float? Well, the most obvious is optimization purposes - dealing with floats as ints tends to be very fast for some purposes if you don't care about corner cases like denormalized numbers. If you're trying to experiment with or study floating point formats, sticking in +Infs, -Infs, NaNs, -0s, denormalized numbers, etc is probably easier or more reliable when done with binary values than literal values or functions.
edit: removed denigration of optimization : )

Wetimer
Member #1,622
November 2001

int f() {
a_union t;
int* ip;
ip = &t.i;
t.d = 3.0; // this assumed not to modify ip, because ip has a different type
// This assumption however is not the case
return *ip;
}

<code>if(Windows.State = Crash) Computer.halt();</code>

Tobi Vollebregt
Member #1,031
March 2001

I've seen -fstrict-aliasing breaking code in a big project. Since rewriting the program was too much work at the time we just used -fno-strict-aliasing when optimizing: that solved the problem.

The breaking code consisted of some checksumming things like this IIRC:

float array[100];
unsigned checksum = 0;
for (int i = 0; i < 100; ++i) {
  checksum = 33*checksum + *(unsigned*)(&array<i>);
}

With -fstrict-aliasing, the resulting checksum would always be 0, because gcc was allowed to assume the unsigned* was at a different location then the float*.

________________________________________
website || Zipfile reader @ Allegro Wiki || Download zipfile reader

orz
Member #565
August 2000

Quote:

I've seen -fstrict-aliasing breaking code in a big project. Since rewriting the program was too much work at the time we just used -fno-strict-aliasing when optimizing: that solved the problem.

The breaking code consisted of some checksumming things like this IIRC:

float array[100];
unsigned checksum = 0;
for (int i = 0; i < 100; ++i) {
  checksum = 33*checksum + *(unsigned*)(&array<i>);
}

With -fstrict-aliasing, the resulting checksum would always be 0, because gcc was allowed to assume the unsigned* was at a different location then the float*.

Interesting. On gcc 3.4.2 on my computer, this works:

void checksum() {
  float array[100];
  for (int i = 0; i < 100; ++i) {
    array<i> = rand() / 65535.;
  }
  unsigned checksum = 0;
  for (int i = 0; i < 100; ++i) {
    checksum = 33*checksum + *(unsigned*)(&array<i>);
  }
  printf("checksum: %d\n", checksum);
}

this does NOT work on my gcc version (due to alliasing issues):

void checksum() {
  float array[100];
  unsigned checksum = 0;
  for (int i = 0; i < 100; ++i) {
    array<i> = rand() / 65535.;
    checksum = 33*checksum + *(unsigned*)(&array<i>);
  }
  printf("checksum: %d\n", checksum);
}

However, wrapping the conversion appears to fix it:

inline unsigned int binfloat2int_ref_cast(float f) {
  return (unsigned int&)f;
}
void checksum() {
  float array[100];
  unsigned checksum = 0;
  for (int i = 0; i < 100; ++i) {
    array<i> = rand() / 65535.;
    checksum = 33*checksum + binfloat2int_ref_cast(array<i>);
  }
  printf("checksum: %d\n", checksum);
}

Casting to a union pointer instead of an integer pointer also appeared to fix the issue.
All versions of the code worked fine on MSVC 7.1.
Of course, sane people would probably make some effort to avoid the issue out of uncertainty just what future compilers will do, or even what current compilers might concievably do if it was the wrong phase of the moon.

Goalie Ca
Member #2,579
July 2002
avatar

Here is a really neat code snipped that quake (and others) have used. Its supposedly really famous.
http://www.beyond3d.com/articles/fastinvsqrt2/

Why is this relevant? Well there were two places it cast from int to float and back and it does so like this which happens to be the first way proposed of doing it.

int i;//some more code after this
float x = *(float*)&i;
return x;

-------------
Bah weep granah weep nini bong!

Go to: