为什么我们在 x86 上将 64 位无符号整数转换为 32 位浮点数时要设置最低有效位?

问题描述 投票:0回答:1
#include <cstddef>
float cast(size_t sz){return sz;}

使用

-mavx2 -O3
在 Clang 13.0.1 上编译上述代码会生成以下代码:

cast(unsigned long):                               # @cast(unsigned long)
        test    rdi, rdi
        js      .LBB0_1
        vcvtsi2ss       xmm0, xmm0, rdi
        ret
.LBB0_1:
        mov     rax, rdi
        shr     rax
        and     edi, 1
        or      rdi, rax
        vcvtsi2ss       xmm0, xmm0, rdi
        vaddss  xmm0, xmm0, xmm0
        ret

GCC、MSVC 和 Intel 编译器以及旧版本也可以生成类似的代码。

我理解该算法的总体目标是解决这样一个事实:在 AVX-512 之前,没有从 64 位无符号 int 到 float 或 double 的转换指令。

因此,如果数字大到足以被解释为负数,则会将其减半,转换,然后加倍。但是,如果在原始整数中设置最低有效位,那么设置它的目的是什么?

这似乎是浪费时间,因为浮点数只有 23 个有效位,并且该位保证不是有效的。也许如果它是增量指令,则在某些情况下可能会影响有效位。但仅仅一个 or 指令似乎没有做任何事情。

c++ assembly clang x86-64 floating-point-conversion
1个回答
2
投票

你假设底部永远不重要——这不是真的。在转换中有一些极端情况值,其中底部位确实很重要。考虑:

#include <iostream>

int main()
{
    uint64_t start = 0x8000000000000000;

    for(uint64_t i=start; i<(start+0x10000000000000ull); i+=0x1000000ull)
    {
        float f = (float)(i);
        float g = (float)(i+1);
        if (f != g)
        {
            std::cout << "From = " << std::hex << i << " we get " << std::hexfloat << f << " and from " << (i+1) << " we get " << std::hexfloat << g << std::endl;
        }
    }
}

这会迭代许多大值,并在底部位产生差异时打印出来(使用方便的十六进制浮点修饰符)。表明有几个需要进行此调整:

From = 8000008000000000 we get 0x1p+63 and from 8000008000000001 we get 0x1.000002p+63
From = 8000028000000000 we get 0x1.000004p+63 and from 8000028000000001 we get 0x1.000006p+63
From = 8000048000000000 we get 0x1.000008p+63 and from 8000048000000001 we get 0x1.00000ap+63
From = 8000068000000000 we get 0x1.00000cp+63 and from 8000068000000001 we get 0x1.00000ep+63
From = 8000088000000000 we get 0x1.00001p+63 and from 8000088000000001 we get 0x1.000012p+63
From = 80000a8000000000 we get 0x1.000014p+63 and from 80000a8000000001 we get 0x1.000016p+63
...

链接:https://godbolt.org/z/jTeo1Ko4s

© www.soinside.com 2019 - 2024. All rights reserved.