我面临这样的情况:需要计算类似于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
这里是uint32_t x=(uint32_t)f
:
cvttss2si rax, xmm0
ret
这里是 int64_t x=(int64_t)f
:
cvttss2si rax, xmm0
ret
最后,这里有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
这最后一个比其他的要复杂得多。此外,Clang和MSVC的行为类似。为了方便起见,我已将其翻译成伪代码:
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;
}
这似乎是在尝试正确计算第一个溢出模64。这似乎有点荒谬,因为cvttss2si的文档告诉我,如果发生溢出(在2^32而不是2 ^ 64处),"返回无限整数值(80000000H)"。
我的问题:
- 这实际上是在做什么,以及为什么要这样做?
- 为什么其他整数类型没有类似的处理方式?
- 如何更改转换以产生类似的代码(仅输出3和4行)(再次假设该值完全可表示)?