2011-11-08 22 views
8

Moja aplikacja została napisana w Delphi5. Używam madExcept do śledzenia błędów. Wyszedłem z wyjątku "Floating point dvision by zero", gdzie nie powinno być. Segment kodu, w którym jest podnoszona, jest następujący:Rejon zmiennoprzecinkowy dzielony przez zero w Delphi5

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

gdzie "c" zawsze ma zawsze wartość "1".

Ślad stos protokołu:

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 

miałem inny wyjątek w jednym punkcie, w którym podział doszło jednak dzielnik był zmienny, co miało również na wartość „1”, gdy wystąpił wyjątek. Że byłem w stanie debugować i rozmnażać.

Moje pytanie: czego mi brakuje? Czy są jakieś fałszywe alarmy dotyczące dzielenia zmiennoprzecinkowego, o których nie wiem?

Ponadto: Nie używam żadnych bibliotek DLL C++ w punktach wyjątku, ponieważ mają one tendencję do obsługi podziałów FP inaczej (zwracanie NaN lub +/- INF zamiast podnoszenia wyjątku).

Wszelkie wskazówki są mile widziane.

+3

Brzmi nieprawdopodobnie. Nie sądzę, aby twoje narzędzia do debugowania wskazywały ci właściwe miejsce, a może zmienne nie trzymają tego, co Twoim zdaniem są. –

+0

Nie pamiętam, czy D5 już go miał, ale czy próbowałeś sprawdzić, co dzieje się w widoku CPU/FPU, kiedy ten kod jest wykonywany? –

+0

@ldsandon Oczywiście D5 już może używać Alt-F2 i wkraczać do widoku CPU/FPU. Dobry pomysł. Ale domyślam się, że nieobsługiwany wyjątek FPU złamie kod 'System._Frac'. –

Odpowiedz

8

prostu próbowałem następujący kod:

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

To po prostu kompiluje i działa zgodnie z oczekiwaniami w Delphi 5.

Domyślam się, że podział na zerowym flaga może być ustawione na zewnątrz kodu (nawet jeśli nie podłączysz się do kodu C++, wywołanie Direct X lub podobnego może mieć ten sam efekt), ale podniesione później, w _Frac.

Jedynym połączeniem do Frac w standardowej realizacji Power() jest przetestowanie Frac(Exponent) = 0.0.

Nastąpiła zmiana w realizacji Frac między Delphi 5 i Delphi 6.

Oto Delphi 5 wersja:

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

Oto wersja 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; 

Z powyższego kodu dowiesz się, że następujące polecenia spowodowały, że opóźnione wyjątki zostały podniesione przed wydaniem Delphi 6: Trunc, Frac, Ceil .

Sądzę, że napotkano problem z Delphi 5, który został naprawiony za pomocą Delphi 6.Być może trzeba będzie użyć własnej wersji Power, jak ten:

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; 
+0

Świetna odpowiedź, która wskazywała na problem dla mnie. Drobne wybijanie nitów, ale odpowiedź można poprawić dzięki komentarzom wyjaśniającym, co robi każda linia montażowa - przynajmniej dla kluczowych linii. Lub alternatywnie, podsumuj, co robi każda procedura _FRAC (przesuń stare CW na stos, ustaw nowe CW, wykonaj operację, pop stare CW ze stosu, tego typu rzeczy). Pomógłby tym z nas, którzy nie byliby dobrze obeznani z montażem x86. :) –

2

Nie ostateczne odpowiedzi za pomocą wszelkich środków, ale ...

FPU podobne wyjątki, w których nie powinno być, może być związane z stosu FPU nie jest prawidłowo rozliczone. Na pewnym etapie mieliśmy podobne problemy, ale zauważyliśmy wyjątki Invalid Floating Point Operation.

Ten artykuł: Delphi bug of the day: FPU stack leak, w którym ktoś znalazł przyczynę wyjątku operacji Nieprawidłowy ruch zmiennoprzecinkowy, zgłoszonego przez S := S + '*';, pomógł nam rozwiązać problem.