【问题标题】:Floating point division by zero exception in Delphi5Delphi5中的浮点除零异常
【发布时间】:2011-11-08 08:10:40
【问题描述】:

我的应用程序是用 Delphi5 编写的。我正在使用 madExcept 来追踪错误。我找到了一个“零浮点除法”异常,它不应该出现在哪里。引发它的代码段如下:

val:=100*Power(1.25,c);

其中 'c' 实际上总是具有值 '1'。

日志的堆栈跟踪:

main thread ($338f8):
00403504 +010 MyApp.exe   System   1970  +5 @FRAC
00479148 +058 MyApp.exe   Math              Power
007ae8a6 +262 MyApp.exe   MyClass  1962 +36 TMyClass.FormMouseWheel

我在某个时候遇到了另一个异常,确实发生了除数,但是除数是一个变量,当异常发生时它的值也为“1”。我能够调试和重现。

我的问题:我错过了什么?是否有一些我不知道的关于浮点除法的误报?

此外:我没有在异常点使用任何 C++ DLL,因为它们倾向于以不同方式处理 FP 除法(返回 NaN 或 +/-INF 而不是引发异常)。

任何指针表示赞赏。

【问题讨论】:

  • 听起来不太合理。我认为您的调试工具没有将您指向正确的位置,或者变量可能不是您认为的那样。
  • 不记得 D5 是否已经拥有它,但是当执行该代码时,您是否尝试检查 CPU/FPU 视图中发生了什么?
  • @ldsandon 当然 D5 已经允许使用 Alt-F2 并进入 CPU/FPU 视图。好主意。但我猜System._Frac 代码中会出现未处理的 FPU 异常。
  • 您是否在使用任何外部 DLL?如果是这样,他们可能已经更改了 FPU 控制字。有关更多信息,请参阅此帖子:wiert.wordpress.com/2009/05/06/…

标签: delphi floating-point division delphi-5 divide-by-zero


【解决方案1】:

我刚刚尝试了以下代码:

procedure TTTest.FormCreate(Sender: TObject);
var v: extended;
    one: extended;
begin
  one := 1.0;
  v := 100*Power(1.25,one);
end;

它只是在 Delphi 5 中按预期编译和运行。

我的猜测是除零标志可能设置在你的代码之外(即使你没有链接到 C++ 代码,调用 Direct X 或类似的可能有相同的效果),但后来在_Frac 中提出。

Power() 的标准实现中对Frac 的唯一调用是测试Frac(Exponent) = 0.0

在 Delphi 5 和 Delphi 6 之间对 Frac 的实现进行了修改。

这是 Delphi 5 版本:

procedure       _FRAC;
asm
    FLD     ST(0)
    SUB     ESP,4
    FSTCW   [ESP]
    FWAIT
    FLDCW   cwChop
    FRNDINT
    FWAIT
    FLDCW   [ESP]
    ADD     ESP,4
    FSUB
end;

这里是 Delphi 6 版本:

procedure       _FRAC;
asm
    FLD     ST(0)
    SUB     ESP,4
    FNSTCW  [ESP].Word     // save
    FNSTCW  [ESP+2].Word   // scratch
    FWAIT
    OR      [ESP+2].Word, $0F00  // trunc toward zero, full precision
    FLDCW   [ESP+2].Word
    FRNDINT
    FWAIT
    FLDCW   [ESP].Word
    ADD     ESP,4
    FSUB
end;

从上面的代码中,你会发现在 Delphi 6 发布之前,以下命令导致了延迟异常:Trunc、Frac、Ceil。

所以我猜您在使用 Delphi 5 时遇到了问题,该问题已在 Delphi 6 中得到修复。您可能必须使用自己的 Power 版本,例如:

function Power(Base, Exponent: Extended): Extended;
begin
  if Exponent = 0.0 then
    Result := 1.0               { n**0 = 1 }
  else if (Base = 0.0) and (Exponent > 0.0) then
    Result := 0.0               { 0**n = 0, n > 0 }
  else
    Result := Exp(Exponent * Ln(Base))
end;

【讨论】:

  • 很好的答案,为我指出了问题所在。轻微的挑剔,但答案可以通过 cmets 解释每条装配线正在做什么来改进 - 至少对于关键线。或者,总结每个 _FRAC 过程的作用(将旧 CW 推入堆栈,设置新 CW,执行操作,从堆栈中弹出旧 CW,诸如此类)。对于我们这些不精通 x86 汇编的人会有所帮助。 :)
【解决方案2】:

无论如何都不是一个确定的答案,但是...

不应该出现的与 FPU 相关的异常可能与未正确清除 FPU 堆栈有关。尽管我们遇到了 Invalid Floating Point Operation 异常,但我们在某个阶段也遇到过类似的问题。

这篇文章:Delphi bug of the day: FPU stack leak 有人追踪了 S := S + '*'; 引发的 Invalid Floating Point Operation 异常的原因,帮助我们解决了这个问题。

【讨论】:

    【解决方案3】:

    您是否在使用 TWebBrowser 或任何 IE 网络浏览器组件,例如 EmbeddedWB?

    如果是这样,这可能会解释它: https://forums.embarcadero.com/thread.jspa?messageID=334125&tstart=0

    它还包含一些可能解决问题的内容,即使您没有使用上面 Jeroen 提供的链接所描述的网络浏览器 (Set8087CW)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多