启用了浮点异常的浮点堆栈处理

时间:2009-10-06 22:07:18

标签: c++ floating-point exception visual-c++

我遇到了在Visual Studio 2005中打开浮点异常的问题。如果我有这样的代码:

  double d = 0.0;
  double d2 = 3.0;
  double d3 = d2/d;

如果我注册了一个SEH处理程序例程,那么我可以轻松地将div-by-zero转换为C ++异常并捕获它。到目前为止一切都很好。

但是,当我这样做时,第一个操作数(上例中的0.0)留在FPU寄存器堆栈上。如果我这样做了8次,那么我将从那时起开始每次浮点操作都会得到一个浮点堆栈检查异常。

我可以使用__asm块来处理这个问题来执行FSTP,从而将杂散值弹出堆栈,一切都很好。

但是,这让我很担心,因为我还没有看到在任何地方讨论。我怎么能确定我应该弹出的值的数量?在异常时弹出堆栈中的所有内容是否安全?这个领域有推荐的最佳实践吗?

谢谢!

1 个答案:

答案 0 :(得分:3)

虽然我也找不到任何东西,但我可以就可能的答案给出一些解释:

ABI定义在函数调用时堆栈应该为空,并且在退出时它应该再次为空,除非返回是一个浮点值,它将是堆栈中唯一的项目。

由于异常处理程序必须能够返回到任何位置,因此必须在这些位置上保留一些条件。这里的问题是,堆栈展开器是否知道具有catch()的函数的FPU堆栈?最有可能的答案是否定的,因为创建具有固定属性的合适返回点比在展开中包含完整FPU堆栈更容易,更快捷。

这会导致你的问题 - 通常引发异常的情况是编译器处理FPU是空的,但是在SEH处理程序中,编译器不知道它导致进入另一个函数,因此无法保重为了以防万一。 (除了它再次非常缓慢)

这意味着FPU堆栈最有可能在返回时处于“一致”状态,这意味着您可能需要等效的EMMS指令。

为何选择EMMS?好吧,除非它不受支持,否则它会执行以下操作:

  • 清除堆栈(修复所有剩余的浮点参数)
  • 清除堆栈标记(在从启用MMX的函数退出时修复了不可用的堆栈)

如果您想支持Pentium 1或更糟,您当然可以在EMMS周围使用(),而不是使用别的东西。

当然不能保证,但我希望我能充分解释可能答案的原因。