C: change type of variable without casting

I am changing uint32_t to float but not changing the actual bits.

Just to be sure, I don't want to leave him. So float f = (float) i

- it's the exact opposite of what I won't do because it changes the bit.

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

What I am doing now and what is already working is:

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
}                                                                      

      

Question . Now I would like to get rid of the variable asInt

, and I would like to know if there is a better / not so ugly way, and then get the address of that variable by casting it twice and casting it over again?

+3


source to share


3 answers


You can try concatenation - if 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 vice versa if you like). I use it a lot when I need to do bitwise operations on one side and still get a uint32_t representation on a number on the other side

[EDIT]
As many commenters have argued, you should consider values ​​that are not represented by integers like NAN, + INF, -INF, +0, -0.

+6


source


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

Assuming your microcontroller has the C standard library with floating point support, you can do this with all the standards without actually using floating point operations, all you need is a function ldexp

that doesn't do any floating point math by itself.

It will look something like this:



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

      

Here we know that IEEE754 floating point numbers have double precision between 2 ^ 23 and 2 ^ 24 (I could be one after the other here, double check please, I am translating this from some work I did in doubles) ... Therefore, the compiler must know how to convert this number to float is trivial. Then ldexp

multiplies that number by 2 ^ -24, just changing the bits in the exponent. No valid floating point and undefined behavior, the code is fully portable to any standard C implementation with IEEE754 numbers. Double check the generated code, but a good compiler library and c shouldn't use floating point instructions here.

If you'd like to peek into some of the experiments I've done around generating random floating point numbers you can take a look at this github repository . It's all about two-seat, but should be trivially translatable for swimming.

+1


source


Reinterpreting the binary representation of an int into a float will lead to serious problems:

  • There are many undefined codes in binary float representation.
  • Other codes are special conditions such as NAN, + INF, -INF, +0, -0 (sic!), Etc.

Also, if it's a random value, even if you catch all the obscene representations, it will give a very bad random distribution.

If you're running an MCU without an FPU, you'd better consider avoiding the float altogether. An alternative could be fractions or scaled integers. There are many implementations of algorithms that use floats, but can be easily converted to fixed-point types with acceptable loss of precision (or even no precision at all). Some may even give more precision than float (note that single precision float only has 23 bits of mantissa, int32 has 31 bits (+ 1 sign for), same for fractional or fixed scaled int.

Note that C11 added (optional) support for _Frac. You might want to investigate this.

Edit:

From your comments, you seem to be converting an int to a float in the range 0 .. <1. To do this, you can assemble the float using bitwise operations on the uint32_t (like the original value). You just need to follow the IEEE format (assuming your toolchain is C standard) See wikipedia .

The result (still uint32_t) can be reinterpreted by union or pointer, as others have already described. Package this into a system-dependent, well-commented library and dig it deep. Remember to check for correctness and alignment (probably the same for float and uint32_t, but important for bit-ops).

0


source







All Articles