您的朴素标量算法不能提供完全正确的转换 - 在某些输入上会遭受双舍入的影响。例如:如果
x
是
0x88000081
,那么正确舍入为浮点数的结果是
2281701632.0f
,但您的标量算法将返回
2281701376.0f
。
我能想到的一个正确的转换如下(如我所说,这只是我脑海中的一个简单示例,可能可以在某个地方节省指令):
movdqa xmm1, xmm0 // make a copy of x
psrld xmm0, 16 // high 16 bits of x
pand xmm1, [mask] // low 16 bits of x
orps xmm0, [onep39] // float(2^39 + high 16 bits of x)
cvtdq2ps xmm1, xmm1 // float(low 16 bits of x)
subps xmm0, [onep39] // float(high 16 bits of x)
addps xmm0, xmm1 // float(x)
常数的值如下:
mask: 0000ffff 0000ffff 0000ffff 0000ffff
onep39: 53000000 53000000 53000000 53000000
这个操作将每个通道的高16位和低16位分别转换为浮点数,然后将这些转换后的值相加。由于每半段仅有16位宽度,所以转换为浮点数不会造成任何舍入误差。当这两个半段相加时才会发生舍入;因为加法是一个正确舍入的操作,所以整个转换都是正确舍入的。
相比之下,您的基础实现首先将低31位转换为浮点数,这会产生一次舍入,然后有条件地将2^31添加到该结果中,这可能会导致第二次舍入。在转换中有两个单独的舍入点时,除非您非常小心地处理它们的出现,否则不应该期望结果能正确地舍入。
int
转换为float
还是将float
转换为int
还是两者兼而有之?你能否更正问题的标题和/或正文,使其不那么含糊不清? - Alexey Frunze