【问题标题】:Coercing float into unsigned char on ARM vs. Intel在 ARM 与 Intel 上强制浮动到 unsigned char
【发布时间】:2011-06-12 17:53:34
【问题描述】:

当我在 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

您可以想象,这种差异可能会在跨平台项目中引入可怕错误。我的问题是:

  1. 我认为将浮点数强制转换为 unsigned char 会在所有平台上产生相同的结果是错误的吗?

  2. 可能是编译器问题?

  3. 有没有优雅的解决方法?

【问题讨论】:

  • 我对这些东西不是很了解,但我的第一反应是在任何架构上的任何平台上尝试将浮点数强制转换为无符号字符是个坏主意。
  • 我不同意你的观点,布赖恩。精度在这里不是问题,但性能是。我正在使用 unsigned char 的“包装”性质保持在 0-255 边界内。这是,AFAIK(并且已经阅读了几篇文章)不是一种不常见的技术。
  • @Zmippie:您看到的行为是“饱和”性质,这在例如SIMD 指令集。

标签: iphone floating-point arm intel coercion


【解决方案1】:

C 标准对您要执行的操作没有非常严格的规则。这是有问题的段落,来自第 6.3.1 算术操作数节(特别是第 6.3.1.4 节实浮点和整数):

当实浮点类型的有限值转换为除 _Bool 以外的整数类型时,小数部分被丢弃(即,该值被截断为 0)。如果整数部分的值不能用整数类型表示,则行为未定义。

对于您要询问的确切案例,甚至还有一个更具体的脚注:

将整数类型的值转换为无符号类型时执行的求余运算不需要在将实浮点类型的值转换为无符号类型时执行。因此,可移植实浮点值的范围是(−1, Utype_MAX+1)

UtypeMAX+1 你的情况是256。您的不匹配案例都是负数。截断后,它们仍然是负数并且超出范围 (-1, 256),因此它们牢牢地处于“未定义行为”区域。即使您展示的某些匹配案例(浮点数大于或等于256)也不能保证有效 - 您只是走运了。

您的编号问题的答案,因此:

  1. 是的,你错了。
  2. 从某种意义上说,这是一个编译器问题,因为您的不同编译器会给出不同的结果,但由于规范允许这样做,我不会真的称这是编译器的错。
  3. 这取决于您想做什么 - 如果您能更好地解释这一点,那么 SO 社区中的某个人几乎肯定能够帮助您。

【讨论】:

  • 没错。这是未定义的行为,简单明了。
  • 感谢大家的回答(斯蒂芬佳能在下面对我自己的回答发表了评论)。我可能会在强制之前将浮点值放在一个合理的范围内,因为从你的 cmets 我知道依赖“未定义的行为”是不好的做法。
【解决方案2】:

我将在我自己的问题中回答 3,但不会将其标记为已接受的答案。诀窍似乎是一个简单的强制转换:

c = (char) f;

使用 (int) 或 (short) 也可以。我仍然有兴趣找出导致此问题的原因:编译器或处理器。

【讨论】:

  • 请注意,这实际上不是一个可移植的修复程序。一旦超出从浮点到整数的任何转换的目标类型的可表示范围,您就会处于未定义的行为中。如果您真的想保证跨平台行为,您可以执行c = f &lt; 0 ? 0 : f &gt; UCHAR_MAX ? UCHAR_MAX : f; 之类的操作,但即使这样也不能确定 NaN 的行为。
【解决方案3】:

在我看来,您正在处理的具体问题类似于字节序。尝试用 c = *((char *)&amp;f + sizeof(float) - 1); 或类似的东西替换一个或另一个实现以获取浮点数的最后一个字节,并查看它是否与另一个平台的结果匹配。

一般而言,行为将取决于处理器的字节顺序、字长和浮点能力,以及编译器如何定位这些。 ARM 是双端的,因此它可能匹配也可能不匹配 IA 字节顺序。似乎也不能普遍保证一种 C 实现支持与另一种相同的浮点格式:Fixed-size floating point types

您是否在生产代码中使用它?我会非常努力地研究为什么需要这样做。一种或另一种类型可能未按预期使用。变通办法不会很优雅。

【讨论】:

  • 字节序不会影响这个问题。此外,iPad 处理器和 Intel 芯片都是 little-endian。
猜你喜欢
  • 2016-03-26
  • 1970-01-01
  • 2021-06-01
  • 1970-01-01
  • 2016-06-03
  • 1970-01-01
  • 2011-02-04
  • 1970-01-01
  • 2012-04-15
相关资源
最近更新 更多