在 ARM 与 Intel 上将 float 强制转换为 unsigned char
当我在 Intel 机器上运行以下 C 代码时...
float f = -512;
unsigned char c;
while ( f < 513 )
{
c = f;
printf( "%f -> %d\n", f, c );
f += 64;
}
...输出如下:
-512.000000 -> 0
-448.000000 -> 64
-384.000000 -> 128
-320.000000 -> 192
-256.000000 -> 0
-192.000000 -> 64
-128.000000 -> 128
-64.000000 -> 192
0.000000 -> 0
64.000000 -> 64
128.000000 -> 128
192.000000 -> 192
256.000000 -> 0
320.000000 -> 64
384.000000 -> 128
448.000000 -> 192
512.000000 -> 0
但是,当我在 ARM 设备(在我的例子中是 iPad)上运行相同的代码时,结果完全不同
-512.000000 -> 0
-448.000000 -> 0
-384.000000 -> 0
-320.000000 -> 0
-256.000000 -> 0
-192.000000 -> 0
-128.000000 -> 0
-64.000000 -> 0
0.000000 -> 0
64.000000 -> 64
128.000000 -> 128
192.000000 -> 192
256.000000 -> 0
320.000000 -> 64
384.000000 -> 128
448.000000 -> 192
512.000000 -> 0
:您可以想象,这种差异可能会在跨平台项目中引入可怕的错误。我的问题是:
我是否错误地假设将浮点数强制转换为无符号字符会在所有平台上产生相同的结果?
他可能是编译器问题吗?
有一个优雅的解决方法吗?
When I run the following C code on an Intel machine...
float f = -512;
unsigned char c;
while ( f < 513 )
{
c = f;
printf( "%f -> %d\n", f, c );
f += 64;
}
...the output is as follows:
-512.000000 -> 0
-448.000000 -> 64
-384.000000 -> 128
-320.000000 -> 192
-256.000000 -> 0
-192.000000 -> 64
-128.000000 -> 128
-64.000000 -> 192
0.000000 -> 0
64.000000 -> 64
128.000000 -> 128
192.000000 -> 192
256.000000 -> 0
320.000000 -> 64
384.000000 -> 128
448.000000 -> 192
512.000000 -> 0
However, when I run the same code on an ARM device (in my case an iPad), the results are quite different:
-512.000000 -> 0
-448.000000 -> 0
-384.000000 -> 0
-320.000000 -> 0
-256.000000 -> 0
-192.000000 -> 0
-128.000000 -> 0
-64.000000 -> 0
0.000000 -> 0
64.000000 -> 64
128.000000 -> 128
192.000000 -> 192
256.000000 -> 0
320.000000 -> 64
384.000000 -> 128
448.000000 -> 192
512.000000 -> 0
As you can imagine, this sort of difference can introduce horrible bugs in cross-platform projects. My questions are:
Was I wrong to assume that coercing a float into an unsigned char would yield the same results on all platforms?
Could his be a compiler issue?
Is there an elegant workaround?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
C 标准对于您想要做的事情没有非常严格的规则。这是来自 6.3.1 算术操作数节(特别是 6.3.1.4 实数浮点和整数节)的相关段落:
关于您所询问的确切案例,甚至还有一个更具体的脚注:
您的情况的
UtypeMAX+1
是256
。你的不匹配案例都是负数。截断后,它们仍然为负,并且超出范围 (-1, 256),因此它们牢牢地处于“未定义行为”区域。即使您展示的一些匹配案例(其中浮点数大于或等于256
)也不能保证有效 - 您只是运气好而已。因此,您的编号问题的答案是:
The C standard doesn't have very hard rules for what you're trying to do. Here's the paragraph in question, from Section 6.3.1 Arithmetic operands (specifically Section 6.3.1.4 Real floating and integer):
There's even a more specific footnote about the exact case you're asking about:
UtypeMAX+1
for your case is256
. Your mismatched cases are all negative numbers. After the truncation, they're still negative and are outside the range (-1, 256), so they're firmly in the 'undefined behaviour' zone. Even some of the matching cases you've shown, where the floating point number is greater than or equal to256
, aren't guaranteed to work - you're just getting lucky.The answers to your numbered questions, therefore:
我将回答我自己的问题中的 3,但不会将其标记为已接受的答案。技巧似乎是强制转换中的简单转换:
使用 (int) 或 (short) 也可以。我仍然有兴趣找出这个问题的原因在哪里:编译器或处理器。
I'm going to answer to 3 in my own question but will not flag that as the accepted answer. The trick seems to be a simple cast in the coercion:
Using (int) or (short) works too. I'm still interested to find out where the cause of this problem lies: compiler or processor.
您正在处理的具体问题对我来说看起来像是字节顺序。尝试用
c = *((char *)&f + sizeof(float) - 1);
或类似的内容替换一个或另一个实现,以获取浮点数的最后一个字节,并查看是否它与其他平台的结果匹配。一般来说,行为取决于处理器的字节顺序、字长和浮点功能,以及编译器如何定位这些功能。 ARM 是双端字节序,因此它可能与 IA 字节顺序匹配,也可能不匹配。似乎也没有一般保证一个 C 实现支持与另一个 C 实现相同的浮点格式: Fixed-浮点类型的大小。
您在生产代码中使用它吗?我会非常努力地研究为什么需要这样做。其中一种类型可能没有按预期使用。解决方法不会很优雅。
The specific issue you're dealing with looks like endianness to me. Try replacing one or the other implementation with
c = *((char *)&f + sizeof(float) - 1);
or something similar to get the last byte of the float, and see if it matches the result for the other platform.In general, the behavior will depend on endianness, word length and floating point capabilities of the processor, and how the compiler targets that. ARM is bi-endian, so it might or might not match IA byte ordering. It seems there's also no general guarantee that one C implementation supports the same floating point format as another: Fixed-size floating point types .
Are you using this in production code? I'd look very hard at why this needs to be done. One or the other type is probably not being used as intended. Workarounds are not going to be elegant.