`uint64_t`有什么困难?(转换装配从`浮动`)

ima*_*ett 7 floating-point assembly

我处于需要计算类似事物的情况size_t s=(size_t)floorf(f);.也就是说,参数是一个浮点数,但它有一个整数值(假设floorf(f)它足够小,可以准确表示).在优化这一点的同时,我发现了一些有趣

以下是从float整数转换(GCC 5.2.0 -O3).为清楚起见,给出的转换是测试函数的返回值.

这是int32_t x=(int32_t)f:

    cvttss2si   eax, xmm0
    ret
Run Code Online (Sandbox Code Playgroud)

这是uint32_t x=(uint32_t)f:

    cvttss2si   rax, xmm0
    ret
Run Code Online (Sandbox Code Playgroud)

这是int64_t x=(int64_t)f:

    cvttss2si   rax, xmm0
    ret
Run Code Online (Sandbox Code Playgroud)

最后,这是uint64_t x=(uint64_t)f;:

    ucomiss xmm0, DWORD PTR .LC2[rip]
    jnb .L4
    cvttss2si   rax, xmm0
    ret
.L4:
    subss   xmm0, DWORD PTR .LC2[rip]
    movabs  rdx, -9223372036854775808
    cvttss2si   rax, xmm0
    xor rax, rdx
    ret

.LC2:
    .long   1593835520
Run Code Online (Sandbox Code Playgroud)

最后一个比其他的复杂得多.此外,Clang和MSVC表现相似.为方便起见,我将其翻译成伪C:

float lc2 = (float)(/* 2^63 - 1 */);
if (f<lc2) {
    return (uint64_t)f;
} else {
    f -= lc2;
    uint64_t temp = (uint64_t)f;
    temp ^= /* 2^63 */; //Toggle highest bit
    return temp;
}
Run Code Online (Sandbox Code Playgroud)

这看起来好像正在尝试正确计算第一个溢出模64.这看起来很虚伪,因为cvttss2si的文档告诉我如果发生溢出(在2 ^ 32,而不是2 ^ 64),"返回不定的整数值(80000000H)".

我的问题:

  1. 这到底是做什么的,为什么?
  2. 为什么其他整数类型没有类似的东西?
  3. 如何更改转换以生成类似的代码(仅输出第3行和第4行)(同样,假设该值可以准确表示)?

Jes*_*ter 8

由于cvttss2si签名转换,它会认为区间中的数字[2^63, 2^64)超出范围,而实际上它们在无符号范围内.因此,检测到这种情况并将其映射到浮点中的低半部分,并且在转换之后应用校正.

至于其他情况,请注意uint32_t转换仍使用64位目标,该目标将适用于整个范围,uint32_t并且根据调用约定使用结果的低32位隐式进一步截断.

在避免额外代码方面,这取决于您的输入是否可能属于上述范围.如果可以的话,就无法绕过它.否则,首先签署的双重演员然后签名到无签名可以工作,即.(uint64_t)(int64_t)f.