【问题标题】:Converting float to uint64 and uint32 behaves strangely将 float 转换为 uint64 和 uint32 的行为很奇怪
【发布时间】:2015-06-05 11:56: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);

除了高位 32 位被截断外,我的行为方式与 64 位转换完全相同。 等于:

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)

【问题讨论】:

  • 无符号数是否溢出?还是叫溢出?
  • 当浮点数表示的数字不适合目标整数时,它是一个FE_OVERFLOW。

标签: c++ floating-point type-conversion


【解决方案1】:

从浮点到整数的转换中的溢出是未定义的行为。您不能依赖于使用单个汇编指令或使用溢出的指令来完成您希望为其设置溢出标志的确切值集。

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

来自Intel manual

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

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

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

blog post 尽管是针对 C 语言的,但它扩展了这个主题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-06
    • 2021-04-01
    • 2017-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-14
    相关资源
    最近更新 更多