Scheintod
Scheintod

Reputation: 8115

c: change variable type without casting

I'm changing an uint32_t to a float but without changing the actual bits.

Just to be sure: I don't wan't to cast it. So float f = (float) i is the exact opposite of what I wan't to do because it changes bits.

I'm going to use this to convert my (pseudo) random numbers to float without doing unneeded math.

What I'm currently doing and what is already working is this:

float random_float( uint64_t seed ) {

    // Generate random and change bit format to ieee
    uint32_t asInt = (random_int( seed ) & 0x7FFFFF) | (0x7E000000>>1);

    // Make it a float
    return *(float*)(void*)&asInt; // <-- pretty ugly and nees a variable
}                                                                      

The Question: Now I'd like to get rid of the asInt variable and I'd like to know if there is a better / not so ugly way then getting the address of this variable, casting it twice and dereferencing it again?

Upvotes: 4

Views: 3170

Answers (3)

too honest for this site
too honest for this site

Reputation: 12263

Reinterpreting the binary representation of an int to a float would result in major problems:

  • There are a lot of undefined codes in the binary representation of a float.
  • Other codes represent special conditions, like NAN, +INF, -INF, +0, -0 (sic!), etc.

Also, if that is a random value, even if catching all non-value representations, that would yield a very bad random distribution.

If you are working on an MCU without FPU, you should better think about avoiding float at all. An alternative might be fraction or scaled integers. There are many implementations of algorithms which use float, but can be easily converted to fixed point types with acceptable loss of precision (or even none at all). Some might even yield more precision than float (note that single precision float has only 23 bits of mantissa, an int32 would have 31 bits (+ 1 sign for either), same for a fractional or fixed scaled int.

Note that C11 added (optional) support for _Frac. You might want to research on that.

Edit:

According you your comments, you seem to convert the int to a float in range 0..<1. For that, you can assemble the float using bit operations on an uint32_t (e.g. the original value). You just need to follow the IEEE format (presumed your toolchain does comply to the C standard! See wikipedia.

The result (still uint32_t) can then be reinterpreted by a union or pointer as described by others already. Pack that in a system-dependent, well-commented library and dig it deep. Do not forget to check about endianess and alignment (likely both the same for float and uint32_t, but important for the bit-ops).

Upvotes: 0

Art
Art

Reputation: 20402

So you seem to want to generate floating point numbers between 0.5 and 1.0 judging from your code.

Assuming that your microcontroller has a standard C library with floating point support, you can do this all standards compliant without actually involving any floating point operations, all you need is the ldexp function that itself doesn't actually do any floating point math.

This would look something like this:

return ldexpf((1 << 23) + random_thing_smaller_than_23_bits(), -24);

The trick here is that we happen to know that IEEE754 binary32 floating point numbers have integer precision between 2^23 and 2^24 (I could be off-by-one here, double check please, I'm translating this from some work I've done on doubles). So the compiler should know how to convert that number to a float trivially. Then ldexp multiplies that number by 2^-24 by just changing the bits in the exponent. No actual floating point operations involved and no undefined behavior, the code is fully portable to any standard C implementation with IEEE754 numbers. Double check the generated code, but a good compiler and c library should not use any floating point instructions here.

If you want to peek at some experiments I've done around generating random floating point numbers you can peek at this github repo. It's all about doubles, but should be trivially translatable to floats.

Upvotes: 1

Ishay Peled
Ishay Peled

Reputation: 2868

You could try union - as long as you make sure the types are identical in memory sizes:

union convertor {
    int asInt;
    float asFloat;
};

Then you can assign your int to asFloat (or the other way around if you want to). I use it a lot when I need to do bitwise operations on one hand and still get a uint32_t representation on the number on the other hand

[EDIT]
Like many of the commentators rightfully state, you must take into consideration values that are not presentable by integers like like NAN, +INF, -INF, +0, -0.

Upvotes: 6

Related Questions