将float转换为unsigned long以访问c #define中的float内部

我想将float转换为unsigned long ,同时保持float的二进制表示(所以我不想将5.05 !)。

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

 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[]类型。

你应该使用一个联盟:

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

(后者会让你传递xl ,你需要一个unsigned long [3]类型的数组unsigned long [3] )。

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

注意:下面建议的一个解决方案是为我的数组使用联合类型。 但是,这没有选择。 我实际上在做以下事情

 #define Calc(x) (((x & 0x7F800000) >> 23) - 127) unsigned long 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 

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


编辑:

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

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

这似乎完全符合你的意图。 在我看来, signed char就足够了,如果不是,那就是int16_t

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.03.0没有地址。

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

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

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

如果它只是你需要的那个数组,那么另一种方法可能就是

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

为什么不自己简单地对数据运行init函数。 您可以在运行时使用计算更新unsigned long表,而不是编译时。

 #include  #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 $ 

按照@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; } 

这打印(根据海湾合作委员会3.4.5,旧我知道:(,但这就是我所拥有的所有地方,使用-O3):

 3f800000 40000000 40400000 800000 

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

 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  ; \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  ; \MSVCRT.printf 

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

据我所知,使用宏无法做到这一点。 另外,如果我没记错的话,C标准并不能保证longfloat将使用相同的位数,因此即使是原始方法在不同的体系结构上也可能不可靠。

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

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

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

 float f = 2.0; unsigned long x; assert(sizeof f == sizeof x); /* STATIC_ASSERT */ memcpy(&x, &f, sizeof x); 

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