在C中将处理器中的float浮动到int

Jav*_*Jav 1 c floating-point optimization casting

我对编译器如何通过如下指令进行float转换感兴趣int:

    float x_f = 3.1415
    int x = (int)x_f;
Run Code Online (Sandbox Code Playgroud)

特别是谈论速度.它是否像内置处理器指令一样超快?还是需要计算?

如果它float总是包含一个精确的整数(ex :),它也会改变x_f = 3.0000.

编辑:这个问题是针对intel x86处理器使用的gcc编译器.

EDIT2:如果有变化x_f = 3.0吗?

R..*_*R.. 9

这在很大程度上取决于特定的cpu.由于您对x86感兴趣,原始的387 fpu有一个将float转换为整数的指令,但它不能直接使用,因为它使用默认的舍入模式,而C中的转换需要截断,而不是舍入.因此,以下功能:

int f(float x)
{
    return x;
}
Run Code Online (Sandbox Code Playgroud)

编译(gcc -O3 -fno-asynchronous-unwind-tables用于避免asm中的crud):

        .text
        .p2align 4,,15
        .globl  f
        .type   f, @function
f:
        subl    $8, %esp
        fnstcw  6(%esp)
        movw    6(%esp), %ax
        movb    $12, %ah
        movw    %ax, 4(%esp)
        flds    12(%esp)
        fldcw   4(%esp)
        fistpl  (%esp)
        fldcw   6(%esp)
        movl    (%esp), %eax
        addl    $8, %esp
        ret
Run Code Online (Sandbox Code Playgroud)

它正在做什么来保存,更改和恢复fpu控制字以更改舍入模式.

另一方面,如果您正在构建具有可用于浮点的SSE的目标,则可以获得:

        .text
        .globl  f
        .type   f, @function
f:
        cvttss2si       4(%esp), %eax
        ret
Run Code Online (Sandbox Code Playgroud)

所以,这真的取决于.

最后,既然你提到你对价值已经是整数的情况特别感兴趣,这没有任何区别.转换的cpu操作几乎肯定不在意.然而,在这种情况下,你可以欺骗:因为你知道输入的是一个整数,四舍五入和截断产生相同的结果,并且可以使用lrintf,而不是铸造或隐式转换为浮动.对于不使用sse进行数学运算的x86目标,这应该是一个重大改进,特别是如果编译器识别lrintf并内联它.这是相同的函数,使用lrintf(x)而不是x,-fno-math-errno添加了选项(否则gcc假设libm可能想要设置errno,因此不会替换调用):

f:
        pushl   %eax
        flds    8(%esp)
        fistpl  (%esp)
        movl    (%esp), %eax
        popl    %edx
        ret
Run Code Online (Sandbox Code Playgroud)

请注意,gcc在编译此函数方面做得不好; 它可能产生:

f:
        flds    4(%esp)
        fistpl  4(%esp)
        movl    4(%esp), %eax
        ret
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为堆栈上的参数空间属于被调用者,可能会随意破坏.即使它不是,movl (%esp),%eax ; popl %edx当你不在乎什么最终edx是一种愚蠢的写作方式popl %eax......

  • @StephenCanon:但是,SSE 不是正常/默认 x86 ABI 的一部分。您必须使用非默认的“-march”和/或“-mfpmath=sse”和“-msse2”进行编译才能让 gcc 生成它。 (2认同)