I want to convert a float
to a unsigned long
, while keeping the binary representation of the float
(so I do not want to cast 5.0
to 5
!).
This is easy to do in the following way:
float f = 2.0;
unsigned long x = *((unsigned long*)&f)
However, now I need to do the same thing in a #define
, because I want to use this later on in some array initialization (so an [inline] function is not an option).
This does not compile:
#define f2u(f) *((unsigned long*)&f)
If I call it like this:
unsigned long x[] = { f2u(1.0), f2u(2.0), f2u(3.0), ... }
The error I get is (logically):
lvalue required as unary ‘&’ operand
Note: One solution that was suggested below was to use a union
type for my array. However, that's no option. I'm actually doing the following:
#define Calc(x) (((x & 0x7F800000) >> 23) - 127)
unsigned long x[] = { Calc(f2u(1.0)), Calc(f2u(2.0)), Calc(f2u(3.0)), ... }
So the array really will/must be of type long[]
.
The problem here is that you're trying to take the address of a constant. Unfortunately, constants are not lvalues, and they do not have an address to take.
As far as I know, there is no way to do this using a macro. Also, if I remember correctly, the C standard does not guarantee that a
long
and afloat
will use the same number of bits, so even your original method may be unreliable on different architectures.You should probably use a union:
or perhaps:
(The latter will let you pass
x.l
where you need an array of typeunsigned long [3]
).Of course you need to ensure that
unsigned long
andfloat
have the same size on your platform.The method you are trying to use is formally illegal. Pointer-based raw memory reinterpretation constitutes so called "type punning", which some compilers will detect and warn you about. Type punning in general case leads to undefined behavior in C. And this is not a theoretical undefined behavior at all, since some compilers rely on this for optimization (see strict value semantics in GCC, for example).
Another variety of type punning is using unions to reinterpered raw memory data. Using unions in that way is formally as illegal (i.e. leads to undefined behavior) as ordinary pointer-based type punning, even though some compilers openly allow it. (Update: TC3 for C99 formally allowed this use of unions.)
The most safe and legal way to inspect object of one type as object of another (unrelated) type is by using
memcpy
. Just copy your source object to your destination object and use/inspect the "copy" instead of the originalThis, of course, is not exactly what you need in your application, since you are looking for something that will work for reinterpretation of constants in an aggregate initializer. However, you have to keep in mind that, firstly, this kind of reinterpretation (in all its forms) is only applicable to lvalues and not to immediate constants. And, secondly, all aggregate initializers in C89/90 (aggregate initializers for static objects in C99) are required to be constants, while reinterpretation does not produce constants.
lvalue
means something assignable.1.0
is a constant, not a variable, and you cannot get reference to it (neither assign to it).Meaning:
This:
Is actually:
and
1.0
,2.0
and3.0
has no address.The problem is not related to
#define
as define is a simple substitution, This code is invalid as well:The problem is that you are trying to reference to immediate values, which have no address.
In this case you won't probably be able to omit a step in-between.
I admit it is not very elegant. But if you run into memory difficulties because of that (embedded sytem?), you can do this separately and automatically create a source file with the correct array.
EDIT:
Yet another solution would be to tell the compiler what you really want. Obviously, you want to calculate the exponent of a floating point number. So you could just do
That seems exactly to do what you intend to do. And it seems to me that a
signed char
woud be enough, and if not, aint16_t
.If it is only this array where you need that, another approach could be