Convert float to unsigned long to access float int

2019-04-09 05:06发布

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[].

8条回答
Viruses.
2楼-- · 2019-04-09 05:41

following along @caf's answer, you can use a union:

#define F2L(x) ((union{float f;unsigned long l;})(x)).l

int main(int argc, char *argv[])
{
    unsigned long array[] = {F2L(1.0f),F2L(2.0f),F2L(3.0f)};
    printf("%x %x %x\n",array[0],array[1],array[2]);
    printf("%x\n",array[1] - array[0]);  
  system("PAUSE");  
  return 0;
}

this prints (under GCC 3.4.5, old I know :(, but thats all I have where I am atm, using -O3):

3f800000 40000000 40400000
800000

and the generated asm confirms its treating them as unsigned longs:

CPU Disasm
Address   Hex dump          Command                                  Comments
004012A8  |.  C745 E8 00008 MOV DWORD PTR SS:[LOCAL.6],3F800000
004012AF  |.  B9 0000803F   MOV ECX,3F800000
004012B4  |.  BA 00004040   MOV EDX,40400000
004012B9  |.  894C24 04     MOV DWORD PTR SS:[LOCAL.13],ECX          ; /<%x> => 3F800000
004012BD  |.  B8 00000040   MOV EAX,40000000                         ; |
004012C2  |.  895424 0C     MOV DWORD PTR SS:[LOCAL.11],EDX          ; |<%x> => 40400000
004012C6  |.  C745 EC 00000 MOV DWORD PTR SS:[LOCAL.5],40000000      ; |
004012CD  |.  C745 F0 00004 MOV DWORD PTR SS:[LOCAL.4],40400000      ; |
004012D4  |.  894424 08     MOV DWORD PTR SS:[LOCAL.12],EAX          ; |<%x> => 40000000
004012D8  |.  C70424 003040 MOV DWORD PTR SS:[LOCAL.14],OFFSET 00403 ; |format => "%x %x %x
"
004012DF  |.  E8 6C050000   CALL <JMP.&msvcrt.printf>                ; \MSVCRT.printf
004012E4  |.  C70424 0A3040 MOV DWORD PTR SS:[LOCAL.14],OFFSET 00403 ; /format => "%x
"
004012EB  |.  8B55 E8       MOV EDX,DWORD PTR SS:[LOCAL.6]           ; |
004012EE  |.  8B45 EC       MOV EAX,DWORD PTR SS:[LOCAL.5]           ; |
004012F1  |.  29D0          SUB EAX,EDX                              ; |
004012F3  |.  894424 04     MOV DWORD PTR SS:[LOCAL.13],EAX          ; |<%x> => 800000
004012F7  |.  E8 54050000   CALL <JMP.&msvcrt.printf>                ; \MSVCRT.printf
查看更多
beautiful°
3楼-- · 2019-04-09 05:56

Why not simply run a init function on the data yourself. You can update the unsigned long table with your calculations during runtime rather then compile time.

#include <stdio.h>

#define Calc(x) (((x & 0x7F800000) >> 23) - 127)

float f[] = {1.0, 2.0, 3.0, 5.0, 250.0, 300.5};
unsigned long *l = (unsigned long *)f;

int main(int argc, const char *argv[])
{
    int i;

    for (i = 0; i < sizeof(f) / sizeof(f[0]); i++)
    {
        printf("[%d] %f %p", i, f[i], l[i]);
        l[i] = Calc(l[i]);
        printf(" | %f %p\n", f[i], l[i]);
    }

    return 0;
}

Sample output:

Andreas Stenius@Neo /opt
$ gcc float2long.c && ./a.exe
[0] 1.000000 0x3f800000 | 0.000000 0x0
[1] 2.000000 0x40000000 | 0.000000 0x1
[2] 3.000000 0x40400000 | 0.000000 0x1
[3] 5.000000 0x40a00000 | 0.000000 0x2
[4] 250.000000 0x437a0000 | 0.000000 0x7
[5] 300.500000 0x43964000 | 0.000000 0x8

Andreas Stenius@Neo /opt
$
查看更多
登录 后发表回答