【问题标题】:Floating point stack handling with floating-point exceptions turned on打开浮点异常的浮点堆栈处理
【发布时间】:2009-10-06 22:07:18
【问题描述】:

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

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

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

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

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

但是,这让我很担心,因为我没有在任何地方看到过这个讨论。我如何确定应该弹出的值的数量?在异常发生时弹出堆栈上的所有内容是否安全?在这方面是否有任何推荐的最佳实践?

谢谢!

【问题讨论】:

  • SEH 也会发生这种情况吗?如果您不注册 SEH 处理程序,行为是否相同?
  • 发生这种情况是因为执行了 _controlfp() 调用,该调用取消了我感兴趣的异常(例如 div-by-zero)。我认为是否注册了 SEH 处理程序并不重要 - 但如果没有,我只会因未处理的异常而崩溃。

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


【解决方案1】:

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

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

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

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

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

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

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

如果您想支持 Pentium 1 或更差的版本,您当然可以在 EMMS 周围使用 if() 并改用其他东西。

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

【讨论】:

  • 这是有道理的——我得出了几乎相同的结论。谢谢。
猜你喜欢
  • 2011-01-14
  • 2021-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多