将 float 转换为 unsigned long 以访问 float 内部结构,在 c #define 中

发布于 2024-11-30 19:33:08 字数 931 浏览 1 评论 0原文

我想将 float 转换为 unsigned long,同时保留 float 的二进制表示形式(所以我不 em> 想要将 5.0 转换为 5!)。

这很容易通过以下方式完成:

float f = 2.0;
unsigned long x = *((unsigned long*)&f)

但是,现在我需要在 #define 中执行相同的操作,因为我想稍后在某些数组初始化中使用它(因此 [inline]函数不是一个选项)。

这不会编译:

#define f2u(f) *((unsigned long*)&f)

如果我这样称呼它:

unsigned long x[] = { f2u(1.0), f2u(2.0), f2u(3.0), ... }

我得到的错误是(逻辑上):

lvalue required as unary ‘&’ operand

注意:下面建议的一种解决方案是使用 union 类型我的阵列。然而,那是没有选择的。我实际上正在执行以下操作:

#define Calc(x) (((x & 0x7F800000) >> 23) - 127)
unsigned long x[] = { Calc(f2u(1.0)), Calc(f2u(2.0)), Calc(f2u(3.0)), ... }

因此数组确实/必须是 long[] 类型。

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

冷清清 2024-12-07 19:33:08

您可能应该使用 union:

union floatpun {
    float f;
    unsigned long l;
};

union floatpun x[3] = { {1.0}, {2.0}, {3.0} };

或者:(

union {
    float f[3];
    unsigned long l[3];
} x = { { 1.0, 2.0, 3.0 } };

后者将让您在需要 unsigned long [3] 类型的数组时传递 xl)。

当然,您需要确保 unsigned longfloat 在您的平台上具有相同的大小。

You should probably use a union:

union floatpun {
    float f;
    unsigned long l;
};

union floatpun x[3] = { {1.0}, {2.0}, {3.0} };

or perhaps:

union {
    float f[3];
    unsigned long l[3];
} x = { { 1.0, 2.0, 3.0 } };

(The latter will let you pass x.l where you need an array of type unsigned long [3]).

Of course you need to ensure that unsigned long and float have the same size on your platform.

高速公鹿 2024-12-07 19:33:08

注意:下面建议的一种解决方案是对我的数组使用联合类型。然而,那是没有选择的。我实际上正在做以下事情

#define Calc(x) (((x & 0x7F800000) >> 23) - 127)
无符号长 x[] = { Calc(f2u(1.0)), Calc(f2u(2.0)), Calc(f2u(3.0)), ... }

所以数组确实/必须是 long[] 类型。

在这种情况下,您可能无法省略中间的步骤。

unsigned float x[] = { 1.0, 2.0, 3.0, ...};
unsigned int y[sizeof x/sizeof x[0]];
for (i=0; i<sizeof x/sizeof x[0]; i++) {
    y[i] = Calc(f2u(x[i]));
}

我承认这不是很优雅。但是,如果您因此遇到内存困难(嵌入式系统?),您可以单独执行此操作并自动创建具有正确数组的源文件。


编辑:

另一种解决方案是告诉编译器您真正想要什么。显然,您想要计算浮点数的指数。所以你可以这样做

#define expo(f) ((long)(log((f)) / log(2)))

,这似乎正是你想做的事情。
在我看来,一个 signed char 就足够了,如果不够的话,一个 int16_t 就足够了。

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

In this case you won't probably be able to omit a step in-between.

unsigned float x[] = { 1.0, 2.0, 3.0, ...};
unsigned int y[sizeof x/sizeof x[0]];
for (i=0; i<sizeof x/sizeof x[0]; i++) {
    y[i] = Calc(f2u(x[i]));
}

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

#define expo(f) ((long)(log((f)) / log(2)))

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, a int16_t.

书间行客 2024-12-07 19:33:08

lvalue 表示可赋值的内容。 1.0 是一个常量,而不是变量,并且您无法获取对它的引用(也无法为其赋值)。

含义:

这:

unsigned long x[3] = { f2u(1.0), f2u(2.0), f2u(3.0) }

实际上是:

unsigned long x[3] = { *((unsigned long*)&1.0, *((unsigned long*)&2.0, *((unsigned long*)&3.0 }

并且 1.02.03.0 没有地址。

该问题与#define 无关,因为定义是一个简单的替换,此代码也是无效的:

unsigned long x = *((unsigned long*)&1.0;

问题是您试图引用没有地址的立即值。

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:

unsigned long x[3] = { f2u(1.0), f2u(2.0), f2u(3.0) }

Is actually:

unsigned long x[3] = { *((unsigned long*)&1.0, *((unsigned long*)&2.0, *((unsigned long*)&3.0 }

and 1.0, 2.0 and 3.0 has no address.

The problem is not related to #define as define is a simple substitution, This code is invalid as well:

unsigned long x = *((unsigned long*)&1.0;

The problem is that you are trying to reference to immediate values, which have no address.

世俗缘 2024-12-07 19:33:08

按照@caf的回答,你可以使用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;
}

这个打印(在GCC 3.4.5下,旧的我知道:(,但这就是我在atm处所拥有的一切) ,使用 -O3):

3f800000 40000000 40400000
800000

并且生成的 asm 确认将它们视为 unsigned long

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

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
百合的盛世恋 2024-12-07 19:33:08

如果您只需要这个数组,则可以使用另一种方法

float x[3] = { 1.0, 2.0, 3.0 };
unsigned long * y = (unsigned long*)&x;

If it is only this array where you need that, another approach could be

float x[3] = { 1.0, 2.0, 3.0 };
unsigned long * y = (unsigned long*)&x;
谷夏 2024-12-07 19:33:08

为什么不直接自己对数据运行 init 函数呢?您可以在运行时而不是编译时使用计算更新无符号长表。

#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;
}

示例输出:

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
$

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
$
生生漫 2024-12-07 19:33:08

这里的问题是你试图获取常量的地址。不幸的是,常量不是左值,并且它们没有可获取的地址。

据我所知,没有办法使用宏来做到这一点。另外,如果我没记错的话,C 标准不保证 longfloat 将使用相同的位数,因此即使您原来的方法也可能不可靠不同的架构。

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 a float will use the same number of bits, so even your original method may be unreliable on different architectures.

野却迷人 2024-12-07 19:33:08

您尝试使用的方法在形式上是非法的。基于指针的原始内存重新解释构成所谓的“类型双关语”,一些编译器会检测到并警告您。一般情况下的类型双关会导致 C 中的未定义行为。这根本不是理论上的未定义行为,因为某些编译器依赖于此进行优化(例如,请参阅 GCC 中的严格值语义)。

另一种类型双关是使用联合来重新解释原始内存数据。以这种方式使用联合在形式上与普通的基于指针的类型双关一样非法(即导致未定义的行为),即使某些编译器公开允许它。 (更新:C99 的 TC3 正式允许使用联合。)

将一种类型的对象检查为另一种(不相关)类型的对象的最安全、合法的方法是使用 memcpy。只需将源对象复制到目标对象并使用/检查“副本”而不是原始对象

float f = 2.0;
unsigned long x;

assert(sizeof f == sizeof x); /* STATIC_ASSERT */
memcpy(&x, &f, sizeof x);

当然,这并不完全是您应用程序中所需要的,因为您正在寻找可以重新解释常量的东西聚合初始值设定项。但是,您必须记住,首先,这种重新解释(所有形式)仅适用于左值,不适用于立即常量。其次,C89/90 中的所有聚合初始值设定项(C99 中静态对象的聚合初始值设定项)都必须是常量,而重新解释不会产生常量。

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 original

float f = 2.0;
unsigned long x;

assert(sizeof f == sizeof x); /* STATIC_ASSERT */
memcpy(&x, &f, sizeof x);

This, 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文