我想将float
转换为unsigned long
,同时保留float
的二进制表示形式(所以我 想要转换{{} 1}}到5.0
!)。
这很容易通过以下方式进行:
5
但是,现在我需要在float f = 2.0;
unsigned long x = *((unsigned long*)&f)
中执行相同的操作,因为我希望稍后在某些数组初始化中使用它(因此[inline]函数不是一个选项)。
这不编译:
#define
如果我这样称呼它:
#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)), ... }
类型。
答案 0 :(得分:2)
你应该使用一个联盟:
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 } };
(后者可让您在需要x.l
类型数组的地方传递unsigned long [3]
。
当然,您需要确保unsigned long
和float
在您的平台上具有相同的尺寸。
答案 1 :(得分:1)
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.0
,2.0
和3.0
没有地址。
问题与#define
无关,因为define是一个简单的替换,此代码也无效:
unsigned long x = *((unsigned long*)&1.0;
问题是您正在尝试引用没有地址的立即值。
答案 2 :(得分:1)
注意:下面建议的一个解决方案是为我的数组使用联合类型。但是,这是没有选择的。我实际上在做以下
#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<sizeof x/sizeof x[0]; i++) {
y[i] = Calc(f2u(x[i]));
}
我承认它不是很优雅。但是如果你遇到内存困难(嵌入式系统?),你可以单独执行此操作并自动创建具有正确数组的源文件。
修改强>
另一个解决方案是告诉编译器你真正想要的是什么。显然,您想要计算浮点数的指数。所以你可以做到
#define expo(f) ((long)(log((f)) / log(2)))
这似乎正是你打算做的。
在我看来,signed char
就足够了,如果没有,int16_t
。
答案 3 :(得分:0)
如果它只是你需要的那个数组,那么另一种方法可能是
float x[3] = { 1.0, 2.0, 3.0 };
unsigned long * y = (unsigned long*)&x;
答案 4 :(得分:0)
为什么不自己简单地对数据运行init函数。您可以在运行时使用计算更新unsigned long表,而不是编译时。
#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
$
答案 5 :(得分:0)
跟着@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,我知道:(,但这就是我所拥有的所有地方,使用-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 <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
答案 6 :(得分:-1)
这里的问题是你试图获取常量的地址。不幸的是,常量不是左值,而且它们没有地址可以使用。
据我所知,使用宏无法做到这一点。另外,如果我没记错的话,C标准并不保证long
和float
将使用相同的位数,因此即使您的原始方法在不同的体系结构上也可能不可靠。
答案 7 :(得分:-1)
您尝试使用的方法是正式非法的。基于指针的原始内存重新解释构成了所谓的“类型惩罚”,一些编译器会检测并警告你。在一般情况下,类型惩罚导致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中静态对象的聚合初始化器)都需要是常量,而重新解释不会产生常量。