当我在C ++中将32位浮点数转换为64位无符号整数时,一切都按预期工作。 溢出导致FE_OVERFLOW标志被设置(cfenv)并返回值0.
std::feclearexcept(FE_ALL_EXCEPT);
float a = ...;
uint64_t b = a;
std::fexcept_t flags;
std::fegetexceptflag(&flags, FE_ALL_EXCEPT);
但是当我将32位浮点数转换为32位无符号整数时,如下所示:
std::feclearexcept(FE_ALL_EXCEPT);
float a = ...;
uint32_t b = a;
std::fexcept_t flags;
std::fegetexceptflag(&flags, FE_ALL_EXCEPT);
我的行为与64位转换完全一样,除了被截断的上部32位。 它等于:
std::feclearexcept(FE_ALL_EXCEPT);
float a = ...;
uint64_t b2 = a;
uint32_t b = b2 & numeric_limits<uint32_t>::max();
std::fexcept_t flags;
std::fegetexceptflag(&flags, FE_ALL_EXCEPT);
因此只有当指数大于或等于64时才会发生溢出 在指数32和64之间,它返回64位转换的低32位而不设置溢出。 这很奇怪,因为你会期望它在指数32处溢出。
这是应该的样子,还是我做错了什么?
编译器是:LLVM版本6.0(clang-600.0.45.3)(基于LLVM 3.5svn)
答案 0 :(得分:1)
从浮点到整数的转换溢出是未定义的行为。您不能依赖于使用单个汇编指令或溢出的指令来完成您希望设置溢出标志的确切值集。
可能已经生成的汇编指令cvttsd2si
确实在溢出时设置了标志,但是在转换为32位int类型时可能会生成指令的64位变体。一个很好的理由是将浮点值截断为无符号 32位整数,如您的问题所示,因为目标寄存器的所有32个低位都设置正确用于浮动 - 执行64位签名指令后,导致转换定义的点值。 cvttsd2si
指令没有无符号变体。
来自Intel manual:
CVTTSD2SI-使用截断标量双精度FP值转换为有符号整数
...
如果转换结果超出了带符号双字整数的范围限制(在非64位模式或64位模式下,REX.W / VEX.W = 0),则会引发浮点无效异常,并且如果屏蔽了此异常,则返回不定的整数值(80000000H)。
如果转换后的结果超出了带符号四字整数的范围限制(在64位模式下,REX.W / VEX.W = 1),则会引发浮点无效异常,如果屏蔽了此异常,则返回不定的整数值(80000000_00000000H)。
此blog post,尽管是针对C,但会扩展此主题。