5

GCC 4.8.0 编译为 32 位。

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

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

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

4

2 回答 2

3

根据 6.3.1.4 (1),行为不是实现定义的,它是未定义的:

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

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

该段落在 C99 中是相同的,只是脚注的编号不同(50)。

对于未定义的行为,在编译时评估的表达式的行为与运行时评估的行为不同的情况并不少见,例如

1 << width_of_type

如果移位距离作为常数表达式给出,则通常评估为 0,如果它是运行时值,则通常评估为 1。

据我所知,导致同一代码出现不同行为的原因是,由于未定义的行为是编译器生成任何东西的许可,它也可以做最简单和/或最快的事情,以及最简单的/fastest 编译期间的事情可能与运行时最简单/最快的事情不同。

于 2013-04-16T14:54:34.810 回答
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

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

于 2013-04-16T15:45:20.387 回答