将 float 转换为 unsigned long 以访问 float 内部结构,在 c #define 中
我想将 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
您可能应该使用 union:
或者:(
后者将让您在需要
unsigned long [3]
类型的数组时传递xl
)。当然,您需要确保
unsigned long
和float
在您的平台上具有相同的大小。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.在这种情况下,您可能无法省略中间的步骤。
我承认这不是很优雅。但是,如果您因此遇到内存困难(嵌入式系统?),您可以单独执行此操作并自动创建具有正确数组的源文件。
编辑:
另一种解决方案是告诉编译器您真正想要什么。显然,您想要计算浮点数的指数。所以你可以这样做
,这似乎正是你想做的事情。
在我看来,一个
signed char
就足够了,如果不够的话,一个int16_t
就足够了。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
.lvalue
表示可赋值的内容。1.0
是一个常量,而不是变量,并且您无法获取对它的引用(也无法为其赋值)。含义:
这:
实际上是:
并且
1.0
、2.0
和3.0
没有地址。该问题与#define 无关,因为定义是一个简单的替换,此代码也是无效的:
问题是您试图引用没有地址的立即值。
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.
按照@caf的回答,你可以使用
union
:这个打印(在GCC 3.4.5下,旧的我知道:(,但这就是我在atm处所拥有的一切) ,使用 -O3):
并且生成的 asm 确认将它们视为
unsigned long
:following along @caf's answer, you can use a
union
:this prints (under GCC 3.4.5, old I know :(, but thats all I have where I am atm, using -O3):
and the generated asm confirms its treating them as
unsigned long
s:如果您只需要这个数组,则可以使用另一种方法
If it is only this array where you need that, another approach could be
为什么不直接自己对数据运行 init 函数呢?您可以在运行时而不是编译时使用计算更新无符号长表。
示例输出:
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.
Sample output:
这里的问题是你试图获取常量的地址。不幸的是,常量不是左值,并且它们没有可获取的地址。
据我所知,没有办法使用宏来做到这一点。另外,如果我没记错的话,C 标准不保证
long
和float
将使用相同的位数,因此即使您原来的方法也可能不可靠不同的架构。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.您尝试使用的方法在形式上是非法的。基于指针的原始内存重新解释构成所谓的“类型双关语”,一些编译器会检测到并警告您。一般情况下的类型双关会导致 C 中的未定义行为。这根本不是理论上的未定义行为,因为某些编译器依赖于此进行优化(例如,请参阅 GCC 中的严格值语义)。
另一种类型双关是使用联合来重新解释原始内存数据。以这种方式使用联合在形式上与普通的基于指针的类型双关一样非法(即导致未定义的行为),即使某些编译器公开允许它。 (更新:C99 的 TC3 正式允许使用联合。)
将一种类型的对象检查为另一种(不相关)类型的对象的最安全、合法的方法是使用
memcpy
。只需将源对象复制到目标对象并使用/检查“副本”而不是原始对象当然,这并不完全是您应用程序中所需要的,因为您正在寻找可以重新解释常量的东西聚合初始值设定项。但是,您必须记住,首先,这种重新解释(所有形式)仅适用于左值,不适用于立即常量。其次,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 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.