Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert or cast a float into its bit sequence such as a long

Good day, I am working in a 16-bit C environment, and I want to convert a float value into its bit sequence such as an integer value. There are multiple ways I know how to achieve this, one is with a union; such as:

union ConvertFloatToInt
{  
   float input;  
   unsigned long  output;  
};

this will "convert" the floating values into a long value, by reading the same memory area, just interpreting it differently.

union ConvertFloatToInt x;
x.input = 20.00;

result

x.output = 0x41A00000;

Other methods are void pointer casts...

float input = 40.00;
unsigned long output;
void* ptr;
ptr = &input;
output = *(unsigned long*) ptr;

result

output = 0x42200000;

This is the idea of what I am trying to do, however, I want the compiler to do the conversion for me, during build, not during run time.

I need a to insert the converted floating data into a constant (const) unsigned long.

I was thinking of trying to convert the float value into a void, and then the void into the unsigned long. Something like this: (and yes this is incorrect, you can not cast to a void)

const unsigned long FloatValue = (unsigned long) ((void) ((float) 20.654));

Is there some way to do this? I was thinking maybe something with void pointers, but all void pointers I know of needs a variable, and variables may not be used in the assignment of const values.

Edit

I am using a C90 compiler. The question is intended in the file scope.

Conclusion

The conclusion was that there is no real solution to this question except when working in the block scope. For which multiple answers were given, and I thank all of you.

My Solution

This is not a good solution, however it solves my problem, but I do not think that this will help many people either. I created a small program for a demonstration purpose. This is not my projects code, and also not the compiler used in my project (before someone says that this is not a C90 compiler)

The compiler used in the demonstration: gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

typedef union
{
            float myfloat;
            unsigned long mylong;
} custom_type;

typedef struct
{
            int a;
            int b;
            custom_type typeA;
            custom_type typeB;
} my_struct;

const my_struct myobj =
{
                            1,2,3.84F,4
};

int main(void)
{
            printf(":: %f\n", myobj.typeA.myfloat);
            printf(":: %ul\n", myobj.typeA.mylong);
            return 0;
}

Output

:: 3.840000
:: 1081459343l

This is little bit crude, however it works in the file scope (but generates warnings).

like image 653
user1574047 Avatar asked Aug 03 '12 13:08

user1574047


2 Answers

You can do this by type-punning through an anonymous union:

unsigned int i = ((union { float f; unsigned int i; }){5.0}).i;

Note that this initialiser is not a constant expression and so cannot be used at file scope.

Type-punning through a union is specified to be allowed by the standard in a footnote:

c11

6.5.2.3 Structure and union members

95) If the member used to read 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.

From a practical point of view, although you cannot use this method to initialise a file-scope constant, you could write an initialisation function that loads the values into file-scope variables at program or module initialisation time.

You're not going to find a portable method that allows you to calculate the values as a compile-time constant expression, because the object representations covered by section 6.2.6 of the standard only apply at run time. Otherwise, a cross-compiler would be required to simulate and not just parametrise the execution environment of its target.


Addendum: this is valid C++, with the condition that the union type must be named:

union u { float f; unsigned int i; };
unsigned int i = u{5.0}.i;

So if you're willing to write in hybrid C/C++ and compile with a C++ compiler, then you can perform the cast at compile time.

like image 59
ecatmur Avatar answered Oct 05 '22 22:10

ecatmur


You can use a C99 compound literal:

const unsigned long FloatValue =
        *(unsigned long *) &(float) {20.654f};

Note that the initializer is not a constant expression so FloatValue can only be declared at block scope and not at file scope.

like image 38
ouah Avatar answered Oct 06 '22 00:10

ouah