为什么C4738没有双重警告?

时间:2012-01-26 14:59:22

标签: c++ visual-c++ floating-point compiler-warnings

Visual C ++可以发出C4738 warning

  

将32位浮点数存储在内存中,可能会导致性能下降

用于将32位float存储在内存中而不是存储在寄存器中的情况。

说明进一步说明使用double可以解决问题。我不明白为什么后者是真的。

为什么在内存中存储float会导致性能下降并且存储double不会?

2 个答案:

答案 0 :(得分:8)

警告结合了两个问题:

  • 浮点数需要存储在内存而不是寄存器中,这会降低性能(因为内存比寄存器慢得多)
  • 浮点数将被舍入(因为寄存器总是有64位或80位,但在内存中浮点数只有32位)。

使用double解决第二个问题(至少部分地,64位仍然不如80位精确),但对可能的性能损失没有影响。这就是警告解释提到两种补救措施的原因:

  

要解决此警告并避免舍入,请使用/ fp:fast或   使用双打而不是花车。

     

要解决此警告并避免用完寄存器,请更改   计算顺序和修改你使用内联

答案 1 :(得分:3)

虽然我不能100%确定原因,但这是我的猜测。

在x86上编译并且未启用SSE2时,编译器必须将x87 FP堆栈用于所有浮点寄存器。在MSVC上,默认情况下,FP模式设置为53位精度舍入。 (我想。我不是百分百肯定的。)

因此,在FP-stack上完成的所有操作都是双精度的。

但是,当某些内容被转换为float时,精度需要舍入为单精度。唯一的方法是通过4字节内存操作数上的fstp指令将其存储到内存中 - 并重新加载它。


让我们看看您链接到的C4738 warning page上的示例:

float func(float f)
{
    return f;
}

int main()
{
    extern float f, f1, f2;
    double d = 0.0;

    f1 = func(d);
    f2 = (float) d;
    f = f1 + f2;   // C4738
    printf_s("%f\n", f);
}

当您致电func()时,d可能存储在x87注册表中。但是,对func()的调用要求将精度降低到单精度。这将导致d舍入/存储到内存。然后在f = f1 + f2;行重新加载并重新提升为双精度。

但是,如果您在整个过程中使用double,编译器可以将d保留在寄存器中 - 从而避免了进出内存的开销。


至于为什么它会让你用完寄存器...我不知道。程序的语义可能会导致具有相同值的双精度值和单精度值 - 在这种情况下,需要额外的寄存器。