Деление с плавающей точкой на нулевое исключение в Delphi5 - PullRequest
8 голосов
/ 08 ноября 2011

Мое приложение написано на 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», когда происходило исключение. Что я смог отладить и воспроизвести.

Мой вопрос: что мне не хватает? Есть ли какие-то ложные срабатывания в делении с плавающей точкой, о которых я не знаю?

Более того: я не использую DLL-библиотеки C ++ в точках исключения, поскольку они имеют тенденцию обрабатывать деления FP по-разному (возвращая NaN или +/- INF, а не вызывая исключение).

Любые указатели оценены.

Ответы [ 3 ]

8 голосов
/ 08 ноября 2011

Я только что попробовал следующий код:

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.

Единственный вызов Frac в стандартной реализации Power() - это проверка Frac(Exponent) = 0.0.

В Delphi 5 и Delphi произошла модификация реализации Frac6.

Вот версия 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;
2 голосов
/ 08 ноября 2011

Не окончательный ответ, но ...

Исключения, связанные с Fpu, там, где их не должно быть, могут быть связаны с тем, что стек FPU не очищен должным образом. У нас были похожие проблемы на одном этапе, хотя мы столкнулись с недопустимыми исключениями операций с плавающей точкой.

Эта статья: Ошибка Delphi дня: утечка стека FPU , когда кто-то выследил причину исключения операции с плавающей запятой, выданной S := S + '*';, помогло нам решить проблему.

0 голосов
/ 08 ноября 2011

Используете ли вы TWebBrowser или какой-либо компонент веб-браузера IE, например EmbeddedWB?

Если это так, это может объяснить это: https://forums.embarcadero.com/thread.jspa?messageID=334125&tstart=0

В нем также есть что-то, что может решить проблему, даже есливы не используете веб-браузер (Set8087CW), как описано выше по ссылке, предоставленной Jeroen.

...