将float转换为uint64和uint32的行为很奇怪

Converting float to uint64 and uint32 behaves strangely

本文关键字:float 转换 uint64 uint32      更新时间:2023-10-16

当我在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);

I的行为与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 version 6.0 (clang-600.0.45.3)(基于LLVM 3.5svn)

从浮点数转换为整数时溢出未定义行为。您不能指望使用单个汇编指令或使用对您希望设置溢出标志的恰好一组值进行溢出的指令来完成此操作。

汇编指令cvttsd2si,可能已经生成,确实在它溢出时设置标志,但是在转换为32位int类型时可能会生成该指令的64位变体。一个很好的理由是,当截断浮点值为无符号 32位整数时,因为在执行64位有符号指令后,对于导致转换定义的浮点值,目标寄存器的所有32位低位都被正确设置为cvttsd2si指令没有无符号的变体。

来自Intel手册:

cvttsd2si -将截断标量双精度FP值转换为有符号整数

如果转换结果超过了带符号双字整数的范围限制(在非64位模式下或在REX.W/vax . w =0的64位模式下),则引发浮点无效异常,如果该异常被屏蔽,则返回不确定整数值(80000000H)。

如果转换结果超出了带符号四字整数的范围限制(64位模式和REX.W/VEX. x .)。W = 1),则引发浮点无效异常,如果该异常被屏蔽,则返回不定整数值(80000000_00000000H)。

这篇博文,尽管是C语言的,但对这个主题进行了扩展。