在x86上将float转换为int的最快方法是什么

rob*_*bor 22 c floating-point optimization x86 assembly

在x86 CPU上将浮点数转换为int的最快方法是什么.优选在C或组件中(可以在C中内嵌)以获得以下任何组合:

  • 32/64/80位浮点数 - > 32/64位整数

我正在寻找一些比让编译器更快的技术.

Zac*_*ame 17

这取决于您是否需要截断转换或舍入转换以及精确度.默认情况下,当您从float转到int时,C将执行截断转换.有FPU指令可以做到这一点,但它不是ANSI C转换,并且使用它有很多警告(例如了解FPU舍入状态).由于你的问题的答案非常复杂,并且取决于你没有表达的一些变量,我推荐这篇文章:

http://www.stereopsis.com/FPU.html


Dar*_*ari 13

使用SSE的打包转换是迄今为止最快的方法,因为您可以在同一指令中转换多个值. ffmpeg有很多组装(主要用于将音频的解码输出转换为整数样本); 检查它的一些例子.


Sum*_*uma 9

普通x86/x87代码的常用技巧是强制浮点的尾数部分表示int.随后是32位版本.

64位版本是类比的.上面发布的Lua版本更快,但依赖于截断double到32位结果,因此它需要将x87单位设置为双精度,并且不能适用于双到64位int转换.

这个代码的好处是它对于符合IEEE 754的所有平台都是完全可移植的,唯一的假设是将浮点舍入模式设置为最接近.注意:便携式的编译和工作.如果有的话,x86以外的平台通常不会从这种技术中受益很多.

static const float Snapper=3<<22;

union UFloatInt {
 int i;
 float f;
};

/** by Vlad Kaipetsky
portable assuming FP24 set to nearest rounding mode
efficient on x86 platform
*/
inline int toInt( float fval )
{
  Assert( fabs(fval)<=0x003fffff ); // only 23 bit values handled
  UFloatInt &fi = *(UFloatInt *)&fval;
  fi.f += Snapper;
  return ( (fi.i)&0x007fffff ) - 0x00400000;
}
Run Code Online (Sandbox Code Playgroud)

  • 在x86上它并不慢,因为生成的代码是相同的.没有FPU指令在x87上立即参数. (3认同)
  • 对于无符号整数,它可以更简单:inline uint32_t toInt(float fval){static float const snapper = 1 << 23; fval + = snapper; return(*(uint32_t*)fval)&0x007FFFFF; } (2认同)

aka*_*ppi 7

如果可以保证运行代码的CPU与SSE3兼容(即使是Pentium 5,JBB),也可以允许编译器使用其FISTTP指令(即-msse3用于gcc).它似乎做了应该总是这样做的事情:

http://software.intel.com/en-us/articles/how-to-implement-the-fisttp-streaming-simd-extensions-3-instruction/

请注意,FISTTP与FISTP不同(它有问题,导致速度慢).它是SSE3的一部分,但实际上是(唯一的)X87端的改进.

除此之外,X86 CPU可能会很好地进行转换.:)

支持SSE3的处理器


dre*_*lax 6

在汇编中有一条指令将浮点转换为int:使用FISTP指令.它将浮点堆栈中的值弹出,将其转换为整数,然后将其存储在指定的地址处.我认为不会有更快的方式(除非你使用像我不熟悉的MMX或SSE这样的扩展指令集).

另一条指令FIST将值留在FP堆栈上,但我不确定它是否适用于四字大小的目的地.


aka*_*ppi 6

Lua代码库有以下代码片段(请访问www.lua.org查看src/luaconf.h).如果你发现(SO发现)更快的方式,我相信他们会很激动.

哦,lua_Number意味着双倍.:)

/*
@@ lua_number2int is a macro to convert lua_Number to int.
@@ lua_number2integer is a macro to convert lua_Number to lua_Integer.
** CHANGE them if you know a faster way to convert a lua_Number to
** int (with any rounding method and without throwing errors) in your
** system. In Pentium machines, a naive typecast from double to int
** in C is extremely slow, so any alternative is worth trying.
*/

/* On a Pentium, resort to a trick */
#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \
    (defined(__i386) || defined (_M_IX86) || defined(__i386__))

/* On a Microsoft compiler, use assembler */
#if defined(_MSC_VER)

#define lua_number2int(i,d)   __asm fld d   __asm fistp i
#define lua_number2integer(i,n)     lua_number2int(i, n)

/* the next trick should work on any Pentium, but sometimes clashes
   with a DirectX idiosyncrasy */
#else

union luai_Cast { double l_d; long l_l; };
#define lua_number2int(i,d) \
  { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; }
#define lua_number2integer(i,n)     lua_number2int(i, n)

#endif

/* this option always works, but may be slow */
#else
#define lua_number2int(i,d) ((i)=(int)(d))
#define lua_number2integer(i,d) ((i)=(lua_Integer)(d))

#endif
Run Code Online (Sandbox Code Playgroud)


the*_*ine 5

我认为需要截断,就像用i = (int)f“C”编写一样。

如果您有 SSE3,您可以使用:

int convert(float x)
{
    int n;
    __asm {
        fld x
        fisttp n // the extra 't' means truncate
    }
    return n;
}
Run Code Online (Sandbox Code Playgroud)

或者,使用 SSE2(或在 x64 中,内联汇编可能不可用),您几乎可以使用同样快的速度:

#include <xmmintrin.h>
int convert(float x)
{
    return _mm_cvtt_ss2si(_mm_load_ss(&x)); // extra 't' means truncate
}
Run Code Online (Sandbox Code Playgroud)

在较旧的计算机上,可以选择手动设置舍入模式并使用普通fistp指令执行转换。这可能只适用于浮点数组,否则必须注意不要使用任何会使编译器更改舍入模式(例如强制转换)的构造。它是这样完成的:

void Set_Trunc()
{
    // cw is a 16-bit register [_ _ _ ic rc1 rc0 pc1 pc0 iem _ pm um om zm dm im]
    __asm {
        push ax // use stack to store the control word
        fnstcw word ptr [esp]
        fwait // needed to make sure the control word is there
        mov ax, word ptr [esp] // or pop ax ...
        or ax, 0xc00 // set both rc bits (alternately "or ah, 0xc")
        mov word ptr [esp], ax // ... and push ax
        fldcw word ptr [esp]
        pop ax
    }
}

void convertArray(int *dest, const float *src, int n)
{
    Set_Trunc();
    __asm {
        mov eax, src
        mov edx, dest
        mov ecx, n // load loop variables

        cmp ecx, 0
        je bottom // handle zero-length arrays

    top:
        fld dword ptr [eax]
        fistp dword ptr [edx]
        loop top // decrement ecx, jump to top
    bottom:
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,内联程序集仅适用于 Microsoft 的 Visual Studio 编译器(也可能是 Borland),必须将其重写为 GNU 程序集才能使用 gcc 进行编译。然而,具有内在函数的 SSE2 解决方案应该非常可移植。

通过不同的 SSE2 内在函数或通过手动将 FPU 控制字设置为不同的舍入模式,可以实现其他舍入模式。