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
我意识到它的实现已定义,但一致性仍然很好。谁能解释一下发生了什么?
答案 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)。
为什么行为可能不同的原因:也许实现复制表示值的位,它意识到它超出范围并退出而不关心保持设置为随机值的符号位。