如何在 x86(32 位)程序集中将无符号整数转换为浮点数?

Posted

技术标签:

【中文标题】如何在 x86(32 位)程序集中将无符号整数转换为浮点数?【英文标题】:How to convert an unsigned integer to floating-point in x86 (32-bit) assembly? 【发布时间】:2012-07-10 04:32:49 【问题描述】:

我需要将 32 位和 64 位 无符号整数 转换为 xmm 寄存器中的浮点值。有 x86 指令可以将 有符号整数 转换为单精度和双精度浮点值,但对于无符号整数则没有。

奖励:如何将 xmm 寄存器中的浮点值转换为 32 位和 64 位无符号整数?

【问题讨论】:

这对于 32 位无符号整数来说很容易。但是 64 位有符号和无符号很难。 float->int 转换同样如此,如果你愿意用NaNINF、溢出等偷工减料,有非常快速的方法... 它是用于编译器的......所以不要偷工减料。 我想唯一的方法是将其分解为低 32 位和高 32 位。对于float->int 转换,您需要分支以捕获所有极端情况。 (或用有条件的移动来破解) 【参考方案1】:

无耻地使用 Janus 答案作为模板(毕竟我真的很喜欢 C++):

在 i7 上使用 gcc -march=native -O3 生成,所以这最多包括 -mavxuint2float 和反之亦然符合预期,长转换仅对大于 263-1 的数字有特殊情况。

0000000000000000 <ulong2double>:
   0:   48 85 ff                test   %rdi,%rdi
   3:   78 0b                   js     10 <ulong2double+0x10>
   5:   c4 e1 fb 2a c7          vcvtsi2sd %rdi,%xmm0,%xmm0
   a:   c3                      retq   
   b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  10:   48 89 f8                mov    %rdi,%rax
  13:   83 e7 01                and    $0x1,%edi
  16:   48 d1 e8                shr    %rax
  19:   48 09 f8                or     %rdi,%rax
  1c:   c4 e1 fb 2a c0          vcvtsi2sd %rax,%xmm0,%xmm0
  21:   c5 fb 58 c0             vaddsd %xmm0,%xmm0,%xmm0
  25:   c3                      retq   

0000000000000030 <ulong2float>:
  30:   48 85 ff                test   %rdi,%rdi
  33:   78 0b                   js     40 <ulong2float+0x10>
  35:   c4 e1 fa 2a c7          vcvtsi2ss %rdi,%xmm0,%xmm0
  3a:   c3                      retq   
  3b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  40:   48 89 f8                mov    %rdi,%rax
  43:   83 e7 01                and    $0x1,%edi
  46:   48 d1 e8                shr    %rax
  49:   48 09 f8                or     %rdi,%rax
  4c:   c4 e1 fa 2a c0          vcvtsi2ss %rax,%xmm0,%xmm0
  51:   c5 fa 58 c0             vaddss %xmm0,%xmm0,%xmm0
  55:   c3                      retq   

0000000000000060 <uint2double>:
  60:   89 ff                   mov    %edi,%edi
  62:   c4 e1 fb 2a c7          vcvtsi2sd %rdi,%xmm0,%xmm0
  67:   c3                      retq   

0000000000000070 <uint2float>:
  70:   89 ff                   mov    %edi,%edi
  72:   c4 e1 fa 2a c7          vcvtsi2ss %rdi,%xmm0,%xmm0
  77:   c3                      retq 

【讨论】:

您只需要 -march=core2-m64 (可能是隐含的,如您的情况)即可获得此结果。此处的所有 AVX 指令都可用于旧版 SSE2 变体。例如,最后一个vcvtsi2ss %rdi,%xmm0,%xmm0 可能是cvtsi2ss %rdi,%xmm0。有趣的是,这也适用于 SSE1,但 uint2double 中的 cvtsi2sd 需要 SSE2。 只有 32 位指令?【参考方案2】:

这是 GCC 生成的内容。我将它们包装在函数中,但您可以轻松删除堆栈处理。并不是所有的都使用SSE来做实际的工作(ulonglong转换没有),如果你找到了相应的说明,请告诉我。 Clang 生成的几乎相同。

% cat tofloats.c 
double ulonglong2double(unsigned long long a) 
    return a;

float ulonglong2float(unsigned long long a) 
    return a;

double uint2double(unsigned int a) 
    return a;

float uint2float(unsigned int a) 
    return a;

% gcc -msse4.2 -g -Os -c tofloats.c && objdump -d tofloats.o
00000000 <ulonglong2double>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 10                sub    $0x10,%esp
   6:   8b 55 0c                mov    0xc(%ebp),%edx
   9:   8b 45 08                mov    0x8(%ebp),%eax
   c:   89 55 f4                mov    %edx,-0xc(%ebp)
   f:   85 d2                   test   %edx,%edx
  11:   89 45 f0                mov    %eax,-0x10(%ebp)
  14:   df 6d f0                fildll -0x10(%ebp)
  17:   79 06                   jns    1f <ulonglong2double+0x1f>
  19:   d8 05 00 00 00 00       fadds  0x0
  1f:   dd 5d f8                fstpl  -0x8(%ebp)
  22:   dd 45 f8                fldl   -0x8(%ebp)
  25:   c9                      leave  
  26:   c3                      ret    

00000027 <ulonglong2float>:
  27:   55                      push   %ebp
  28:   89 e5                   mov    %esp,%ebp
  2a:   83 ec 10                sub    $0x10,%esp
  2d:   8b 55 0c                mov    0xc(%ebp),%edx
  30:   8b 45 08                mov    0x8(%ebp),%eax
  33:   89 55 f4                mov    %edx,-0xc(%ebp)
  36:   85 d2                   test   %edx,%edx
  38:   89 45 f0                mov    %eax,-0x10(%ebp)
  3b:   df 6d f0                fildll -0x10(%ebp)
  3e:   79 06                   jns    46 <ulonglong2float+0x1f>
  40:   d8 05 00 00 00 00       fadds  0x0
  46:   d9 5d fc                fstps  -0x4(%ebp)
  49:   d9 45 fc                flds   -0x4(%ebp)
  4c:   c9                      leave  
  4d:   c3                      ret    

0000004e <uint2double>:
  4e:   55                      push   %ebp
  4f:   89 e5                   mov    %esp,%ebp
  51:   83 ec 08                sub    $0x8,%esp
  54:   66 0f 6e 45 08          movd   0x8(%ebp),%xmm0
  59:   66 0f d6 45 f8          movq   %xmm0,-0x8(%ebp)
  5e:   df 6d f8                fildll -0x8(%ebp)
  61:   c9                      leave  
  62:   c3                      ret    

00000063 <uint2float>:
  63:   55                      push   %ebp
  64:   89 e5                   mov    %esp,%ebp
  66:   83 ec 08                sub    $0x8,%esp
  69:   66 0f 6e 45 08          movd   0x8(%ebp),%xmm0
  6e:   66 0f d6 45 f8          movq   %xmm0,-0x8(%ebp)
  73:   df 6d f8                fildll -0x8(%ebp)
  76:   c9                      leave  
  77:   c3                      ret

以下是奖励积分(转换为整数):

% cat toints.c                                      
unsigned long long float2ulonglong(float a) 
    return a;

unsigned long long double2ulonglong(double a) 
    return a;

unsigned int float2uint(float a) 
    return a;

unsigned int double2uint(double a) 
    return a;

% gcc -msse4.2 -g -Os -c toints.c && objdump -d toints.o  
00000000 <float2ulonglong>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   53                      push   %ebx
   4:   83 ec 0c                sub    $0xc,%esp
   7:   d9 45 08                flds   0x8(%ebp)
   a:   d9 05 00 00 00 00       flds   0x0
  10:   d9 c9                   fxch   %st(1)
  12:   db e9                   fucomi %st(1),%st
  14:   73 0d                   jae    23 <float2ulonglong+0x23>
  16:   dd d9                   fstp   %st(1)
  18:   dd 4d f0                fisttpll -0x10(%ebp)
  1b:   8b 45 f0                mov    -0x10(%ebp),%eax
  1e:   8b 55 f4                mov    -0xc(%ebp),%edx
  21:   eb 13                   jmp    36 <float2ulonglong+0x36>
  23:   de e1                   fsubp  %st,%st(1)
  25:   dd 4d f0                fisttpll -0x10(%ebp)
  28:   8b 55 f4                mov    -0xc(%ebp),%edx
  2b:   8b 45 f0                mov    -0x10(%ebp),%eax
  2e:   8d 8a 00 00 00 80       lea    -0x80000000(%edx),%ecx
  34:   89 ca                   mov    %ecx,%edx
  36:   83 c4 0c                add    $0xc,%esp
  39:   5b                      pop    %ebx
  3a:   5d                      pop    %ebp
  3b:   c3                      ret    

0000003c <double2ulonglong>:
  3c:   55                      push   %ebp
  3d:   89 e5                   mov    %esp,%ebp
  3f:   53                      push   %ebx
  40:   83 ec 0c                sub    $0xc,%esp
  43:   dd 45 08                fldl   0x8(%ebp)
  46:   d9 05 00 00 00 00       flds   0x0
  4c:   d9 c9                   fxch   %st(1)
  4e:   db e9                   fucomi %st(1),%st
  50:   73 0d                   jae    5f <double2ulonglong+0x23>
  52:   dd d9                   fstp   %st(1)
  54:   dd 4d f0                fisttpll -0x10(%ebp)
  57:   8b 45 f0                mov    -0x10(%ebp),%eax
  5a:   8b 55 f4                mov    -0xc(%ebp),%edx
  5d:   eb 13                   jmp    72 <double2ulonglong+0x36>
  5f:   de e1                   fsubp  %st,%st(1)
  61:   dd 4d f0                fisttpll -0x10(%ebp)
  64:   8b 55 f4                mov    -0xc(%ebp),%edx
  67:   8b 45 f0                mov    -0x10(%ebp),%eax
  6a:   8d 8a 00 00 00 80       lea    -0x80000000(%edx),%ecx
  70:   89 ca                   mov    %ecx,%edx
  72:   83 c4 0c                add    $0xc,%esp
  75:   5b                      pop    %ebx
  76:   5d                      pop    %ebp
  77:   c3                      ret    

00000078 <float2uint>:
  78:   55                      push   %ebp
  79:   89 e5                   mov    %esp,%ebp
  7b:   83 ec 08                sub    $0x8,%esp
  7e:   d9 45 08                flds   0x8(%ebp)
  81:   dd 4d f8                fisttpll -0x8(%ebp)
  84:   8b 45 f8                mov    -0x8(%ebp),%eax
  87:   c9                      leave  
  88:   c3                      ret    

00000089 <double2uint>:
  89:   55                      push   %ebp
  8a:   89 e5                   mov    %esp,%ebp
  8c:   83 ec 08                sub    $0x8,%esp
  8f:   dd 45 08                fldl   0x8(%ebp)
  92:   dd 4d f8                fisttpll -0x8(%ebp)
  95:   8b 45 f8                mov    -0x8(%ebp),%eax
  98:   c9                      leave  
  99:   c3                      ret    

函数从堆栈中获取输入并通过堆栈返回。如果您需要在函数结束时将结果保存在 XMM 寄存器中,您可以使用 movd/movq 将它们从堆栈中取出到 XMM。如果函数返回双精度,则结果为 -0x8(%ebp)。如果是浮点数,则结果为 -0x4(%ebp)。 Ulonglong 具有双精度数的长度,整数具有浮点数的长度。

fisttpll:存储带截断的整数

FISTTP 使用截断将 ST 中的值转换为有符号整数 (chop) 作为舍入模式,将结果传输到目的地,并且 流行ST。 FISTTP 接受字、短整数和长整数 目的地。

fucomi:比较浮点值并设置 EFLAGS

对寄存器 ST(0) 的内容进行无序比较 和 ST(i) 并在 EFLAGS 中设置状态标志 ZF、PF 和 CF 根据结果​​注册(见下表)。的标志 比较时忽略零,因此 –0.0 等于 +0.0。

【讨论】:

有趣的答案;但是问题是将一个无符号整数加载到 XMM 寄存器中。 我会接受这个答案;但是有没有办法在没有任何 x87 FP 寄存器的情况下做到这一点?

以上是关于如何在 x86(32 位)程序集中将无符号整数转换为浮点数?的主要内容,如果未能解决你的问题,请参考以下文章

如何以优雅有效的方式将无符号/有符号整数/长整数转换为 C 字符串?

使用啥 ffmpeg 命令将无符号整数列表转换为音频文件?

如何将无符号整数加载到 SIMD 中

Python:如何将 32 位有符号长整数转换为 7 位整数

如何从 Python 将无符号值发送到 dBus

如何在 AVX2 中将 32 位无符号整数转换为 16 位无符号整数?