How to convert or cast a float into its bit sequen

2020-03-21 09:45发布

问题:

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).

回答1:

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.



回答2:

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.



回答3:

I am assuming that these floats are constants and therefore you could just write a small program to do it as a one off exercise - generate the output as required. From that small program do a cut'n'paste job into the other code.

If you have a lot of them, why not just write a script to create the appropriate file for C.



回答4:

You should know somethings about IEEE floating point standards. http://en.wikipedia.org/wiki/IEEE_floating-point_standard

get the fractions bits and get the exponent bits and process them into a long



回答5:

You could achieve your purpose by defining a float constant and then a macro:

const float _FloatValue = 20.654;
#define FloatValueL *((unsigned long *) &_FloatValue)