8

当我在 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. 有没有优雅的解决方法?

4

3 回答 3

9

C 标准对您尝试做的事情没有非常严格的规则。这是有问题的段落,来自第6.3.1 节算术操作数(特别是第6.3.1.4 节 Real floating and integer):

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

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

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

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

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

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

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

c = (char) f;

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

于 2011-01-20T21:12:31.177 回答
-1

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

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

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

于 2011-01-20T21:30:36.283 回答