无符号64位到双转换:为什么这个算法来自g ++

Mat*_*aws 11 floating-point assembly g++ x86-64

如果我编译,使用g ++ 4.9.2

bool int_dbl_com(const unsigned long long x, const double y)
{
    return x <= y;
}
Run Code Online (Sandbox Code Playgroud)

那么汇编程序输出(对于Windows x64调用约定)是:

testq     %rcx, %rcx            # x in RCX
js        .L2
pxor      %xmm0, %xmm0
cvtsi2sdq %rcx, %xmm0
ucomisd   %xmm0, %xmm1          # y in XMM1
setae     %al
ret
Run Code Online (Sandbox Code Playgroud)

该命令cvtsi2sdq签名转换,第一个测试和跳转组合是检查是否%rcx < 0.如果是这样,我们去L2,我不明白:

.L2:
movq       %rcx, %rax
andl       $1, %ecx
pxor       %xmm0, %xmm0
shrq       %rax
orq        %rcx, %rax
cvtsi2sdq  %rax, %xmm0
addsd      %xmm0, %xmm0
ucomisd    %xmm0, %xmm1
setae      %al
ret
Run Code Online (Sandbox Code Playgroud)

天真的,您可能会减半%rcx,转换为double %xmm0,然后添加%xmm0到自身以获取原始值(当然,接受您已经丢失了从64位整数到64位的一些低阶精度浮动).

但这不是代码所做的:它似乎保存了最低位,%rcx然后将其返回到结果中.为什么??那么为什么这些低阶位将会丢失(或者我错在这里)呢?

(无论优化如何,似乎都使用相同的算法;我在这里使用了-O3以便于查看.)

Pas*_*uoq 16

.L2:
movq       %rcx, %rax
andl       $1, %ecx       ; save the least significant bit of %rax
pxor       %xmm0, %xmm0
shrq       %rax           ; make %rax represent half the original number, as a signed value
orq        %rcx, %rax     ; “round to odd”: if the division by two above was not exact, ensure the result is odd
cvtsi2sdq  %rax, %xmm0    ; convert to floating-point
addsd      %xmm0, %xmm0   ; multiply by two
ucomisd    %xmm0, %xmm1   ; compare …
setae      %al
ret
Run Code Online (Sandbox Code Playgroud)

最后三条指令实现<=return源自源代码.其他的人是从转换的一部分uint64_tdouble.

难以理解的步骤是我评论为"圆到奇"的步骤."舍入到奇数"是一种可以防止"双舍入"的恶劣影响的技术.

实际上,该算法将从64位转换为63位,然后从63位转换为IEEE 754 binary64的53位有效数.如果天真地实现,在某些情况下,这两个转换可以产生不同于从64位整数到具有53位有效数的浮点的直接单次转换的结果.这种现象被称为"双舍入".

舍入到奇数确保中间舍入的结果不是在双舍入的情况下将在错误的方向上舍入的值.这足以使所有输入的序列等效:

64-bit ---(round to odd)---> 63-bit ---(round to nearest even)----> binary64 
64-bit -(round-to-nearest-even,the conversion the compiler wants)-> binary64
Run Code Online (Sandbox Code Playgroud)

要回答您问题的其他方面:

但这不是代码所做的:它似乎保存了最低位,%rcx然后将其返回到结果中.为什么??那么为什么这些低阶位将会丢失(或者我错在这里)呢?

这正是在这个特定实例中如何实现round-to-odd.%rcx如果移位不是精确除以2,则最低有效位是1,并且在这种情况下,结果必须为奇数.

无论优化如何,似乎都使用相同的算法; 我在这里使用-O3让它更容易看到.

指令序列是最佳的(据我所知,对于现代处理器)并且对应于从uint64_tint到的源级转换double.即使在最低优化级别,编译器也不需要使用它.优化会发生什么(但这里不会发生)是指令与其他与其他源级构造相对应的指令融合在一起.但是没有必要使用不同的指令序列来生成转换的最佳指令序列-O0.