可能不一致的铸造行为

时间:2013-04-16 14:48:52

标签: c gcc mingw-w64

GCC 4.8.0编译/为32位。

我发现案例 2 6 的行为令人困惑:

int16_t s16 = 0;
double dbl = 0.0;

s16 = (int16_t)((double)32767.0); // 1: s16 = 32767
s16 = (int16_t)((double)32768.0); // 2: s16 = 32767
s16 = (int16_t)((double)-32768.0); // 3: s16 = -32768
s16 = (int16_t)((double)-32769.0); // 4: s16 = -32768

dbl = 32767.0;
s16 = (int16_t)dbl; // 5: s16 = 32767
dbl = 32768.0;
s16 = (int16_t)dbl; // 6: s16 = -32768  
dbl = -32768.0;
s16 = (int16_t)dbl; // 7: s16 = -32768
dbl = -32769.0;
s16 = (int16_t)dbl; // 8: s16 = -32768

我意识到它的实现已定义,但一致性仍然很好。谁能解释一下发生了什么?

2 个答案:

答案 0 :(得分:3)

根据6.3.1.4(1):

,行为不是实现定义的,它是未定义的
  

如果整数部分的值不能用整数类型表示,则行为是未定义的。 61)

     

61)当将实数浮点类型的值转换为无符号类型时,无需执行将整数类型的值转换为无符号类型时执行的剩余操作。就这样   便携式实际浮动值的范围是(−1, Utype_MAX+1)

该段在C99中是相同的,只是脚注有不同的数字(50)。

对于未定义的行为,在编译时计算的表达式的行为与运行时评估的行为不同,例如

1 << width_of_type
如果移位距离作为常量表达式给出,则

通常被评估为0;如果是运行时值,则

被评估为1。

据我所知,导致同一代码的不同行为的原因是,由于未定义的行为是编译器生成任何内容的许可,因此它也可以做最简单和/或最快的事情,编译过程中最简单/最快的事情可能与运行时最简单/最快的事情不同。

答案 1 :(得分:0)

正如已经回答的那样,“未定义”并不意味着“实施已定义”。

如果您的值存在超出限制的风险,那么您应该在转换之前验证范围。 或者您可以使用某些库,例如使用像itrunc(double)这样的boost数学函数你应该在超出范围时得到boost :: math :: rounding_error(不幸的是我不知道你是否可以使用你的int16_t)。

http://www.boost.org/doc/libs/1_53_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/rounding/trunc.html

为什么行为可能不同的原因:也许实现复制表示值的位,它意识到它超出范围并退出而不关心保持设置为随机值的符号位。