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

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

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

===============>>#1 票数:17 已采纳

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

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

===============>>#2 票数:13

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

===============>>#3 票数: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;
}

===============>>#4 票数: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的处理器

===============>>#5 票数:6

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

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

===============>>#6 票数: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

===============>>#7 票数:4

我假设需要截断,就像在“C”中写入i = (int)f

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

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

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

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

在较旧的计算机上,可以选择手动设置舍入模式并使用普通的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:
    }
}

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

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

===============>>#8 票数:3

由于MS在X64中使我们脱离内联汇编并迫使我们使用内在函数,因此我查找了要使用的内容。 MSDN doc给出了_mm_cvtsd_si64x的一个例子。

这个例子有效,但效率非常低,使用2个双倍的未对齐加载,我们只需要一个加载,因此摆脱了额外的对齐要求。 然后产生了许多不必要的负载和重新加载,但它们可以如下消除:

 #include <intrin.h>
 #pragma intrinsic(_mm_cvtsd_si64x)
 long long _inline double2int(const double &d)
 {
     return _mm_cvtsd_si64x(*(__m128d*)&d);
 }

结果:

        i=double2int(d);
000000013F651085  cvtsd2si    rax,mmword ptr [rsp+38h]  
000000013F65108C  mov         qword ptr [rsp+28h],rax  

可以在没有内联汇编的情况下设置舍入模式,例如

    _control87(_RC_NEAR,_MCW_RC);

舍入到最近的是默认值(无论如何)。

我想,是否要在每次通话中设置舍入模式或假设它将被恢复(第三方库)的问题必须通过经验来回答。 您必须为_control87()和相关常量包含float.h

并且,不,这不会在32位中工作,因此请继续使用FISTP指令:

_asm fld d
_asm fistp i

===============>>#9 票数:3

如果你真的关心它的速度,请确保你的编译器正在生成FIST指令。 在MSVC中,您可以使用/ QIfist执行此操作, 请参阅此MSDN概述

您还可以考虑使用SSE内在函数为您完成工作,请参阅英特尔的这篇文章: http//softwarecommunity.intel.com/articles/eng/2076.htm

===============>>#10 票数:-7

通常,您可以信任编译器高效且正确。 通常可以通过为编译器中已存在的东西滚动自己的函数来获得任何东西。

  ask by robottobor translate from so

未解决问题?本站智能推荐:

2回复

在x86上给出无分支FP最小值和最大值的指令是什么?

引用(感谢作者开发和共享算法!): https://tavianator.com/fast-branchless-raybounding-box-intersections/ 由于现代浮点指令集可以计算最小和最大而无分支 作者对应的代码只是 我熟悉例如_mm_ma
4回复

在x86上做水平浮点矢量和的最快方法

你有一个三(或四)个浮点数的向量。 总结它们的最快方法是什么? SSE(movaps,shuffle,add,movd)总是比x87快吗? SSE4.2中的水平加法说明值得吗? 移动到FPU的成本是多少,然后是faddp,faddp? 什么是最快的特定指令序列? “尝试安排事
1回复

将浮点文字转换为x86汇编中的int表示形式?

下面的C代码: int main() { float f; f = 3.0; } 转换为以下汇编指令: main: pushl %ebp movl %esp, %ebp subl $16, %esp flds .LC0 fstps -4(%ebp)
1回复

x86可以独立或并行执行FPU操作吗?

我的老师声称处理器有时可以并行进行FPU操作。 像这样: 所以,正如我所听到的,上面的2个添加操作将比以下更快地执行: 因为处理器必须等到c得到计算。 我想验证这一点,所以我编写了一个执行第二件操作的函数,它通过检查时间戳计数器来测量时间: 那些不是独立的。 现
5回复

测试双数是否为整数的最快方法是什么(在现代英特尔X86处理器中)

我们的服务器应用程序在热代码路径中进行了大量的整数测试,目前我们使用以下函数: 这个功能在我们的工作量中非常热,所以我希望它尽可能快。 如果可以的话,我也想删除“楼层”库调用。 有什么建议?
1回复

为什么Math.h中的cos功能比x86 fcos指令快

math.h中的cos()运行速度高于x86 asm fcos。 以下代码是对math.h中的x86 fcos和cos()进行比较。 在此代码中,1000000次asm fcos耗时150ms; 1000000次cos()调用仅花费80毫秒。 在x86中如何实现fcos?
1回复

在x86_64组装问题中添加双打

您好,我正在尝试学习汇编语言,并学习如何在x86_64中使用浮点数。 据我了解,参数在xmm0,xmm1,xmm2等中传递,结果在xmm0中返回。 因此,我正在尝试制作一个简单的汇编函数,以使其加倍。 这是功能 这也是我正在使用的C代码。 我一直在尝试遵循gdb中发生的事情
2回复

浮点到双精度(IEEE754)转换

我正在尝试在x86体系结构上的asm中将32位float转换为64位double 。 转换由用asm编写的函数完成,然后我想从C调用它。我不知道我在做什么错,但是dst指向的内存似乎保持不变,并且在printf程序崩溃之后。 我想做到这一点而无需任何浮点指令。 这是代码: 和C代码
2回复

在x86和x64上读取同一页面内的缓冲区末尾是否安全?

如果允许在输入缓冲区末尾读取少量数据 ,则可以(并且)简化在高性能算法中找到的许多方法。 这里,“少量”通常意味着超过结尾的W - 1个字节,其中W是算法的字节大小(例如,对于处理64位块中的输入的算法,最多7个字节)。 很明显, 写入输入缓冲区的末尾通常是不安全的,因为您可能会破坏缓冲
3回复

在gcc内联asm中data32 data32 nopw%cs:0x0(%rax,%rax,1)指令是什么意思?

在为gcc编译器的-O2优化运行一些测试时,我在反汇编代码中观察到以下指令: 这个指令做了什么? 为了更详细,我试图了解编译器如何使用O2优化优化无用的递归,如下所示: 上面的代码在没有优化的情况下编译时导致堆栈溢出,但适用于O2优化代码。 我认为使用O2它完全删除了