Jump to content
mjwallin47

Delphi 5 FOR Loop problem (W10-64bit)

Recommended Posts

I have been maintaining an old Delphi 5 application under Windows 10 (64bit) for bank for a number of years and have recently run what looks like a show stopper:

I am finding an anomalous behavior in a FOR loop with Delphi 5 under Windows 10 64bit.   Here is the simplified code:
FOR i := nMin TO nEnd Do
        Begin
          // Code
        End 

The problem is that the code inside the begin..end is being run even if the range of the loop 0 to 0.  To test this, I set both nMin and nEnd to 0 and the code inside the Begin..End is still being executed.  I am also getting Invalid Op Exceptions.

I know that Delphi 5 is way outdated but my client that has ignored my warnings for years and now is apparently going to have to pay the piper. 

Has anybody else run into this behavior with Delphi 5?

Share this post


Link to post

What would you expect, from 0 to 0 allows for 0 as iteration value. Correct behaviour, back since Pascal times...

 

Share this post


Link to post

Thanks for replying - I have looked for this in various Delphi manuals, help files and references online and none of them state what you said but I believe you.  The code I am maintaining was originally written 20 years ago and I never had a need to change any of the complex algorithms related to financial calculations and assumed they were working as intended.  I was trying to track down an Exception (InvalidOp) that has appeared recently in a part of the code that is run only every two years and it is the routine that has the suspected loop problem.

 

The InvalidOp exception may not be related to this loop code but in trying to debug this Exception, I am trying everything.  I am not sure that Delphi 5 is the problem but I have been running this code in Windows 10 64bit for some time.  I had a similar problem with another D5 app years ago that nearly drove me nuts.

Share this post


Link to post

http://docwiki.embarcadero.com/RADStudio/en/Declarations_and_Statements_(Delphi)#For_Statements

 

Quote

For purposes of controlling the execution of the loop, the expressions initialValue and finalValue are evaluated only once, before the loop begins. Hence, the for...to statement is almost, but not quite, equivalent to this while construction:


 begin
   counter := initialValue;
   while counter <= finalValue do
   begin
     ... {statement};
     counter := Succ(counter);
   end;
 end.

 

 

Share this post


Link to post

Regarding my InvalidOp exception which was my orginal problem, I am having a hard time tracking down the exact location of the problem.  The debugger gives me this:

 

procedure _INT

asm    SUB   ESP,4

FSTCW   [ESP]

FWAIT

FLDCW  cwChop

FRNDINT

FWAIT

FLDCW  [EXP]

ADD   ESP,4

 

I am not sure how this helps me find what's going on.  Any assembler experts out there who could lend a hand?

Thanks

 

Share this post


Link to post

Thanks Primoz,

That helps me understand Delphi Loops better.  Apparently the code works the way it is supposed to, but I think all of the ROUND and INT functions are at the root of my problem.  There are a lot of places where code like the following horrors exist:

 

         FOR Age := Round(EntryAge) TO Round(RetireAge) Do
                 Begin
                    PVVBEA := PVVBEA + 12 * tBenRa[Age] * VP[Age] * Rate.QW[Age] *
                              Rate.AM[Round(RetireAge), PartGender] *
                              PMEA[Round(RetireAge - EntryAge)] / PMEA[Round(Age - EntryAge)] *
                              Rate.V[Round(RetireAge - Age)] * PTEA[Round(Age - EntryAge)] /
                              PTEA[Round(EntryAge - EntryAge)] * Rate.V[Round(Age - EntryAge)];
                 End;

 

Its pretty ugly and there are numerous places where a division by zero or FPU errors could happen.

Share this post


Link to post

WTF? Who stores ages as floating point and then uses arrays (which of course use integer indexing) for lookups? And this is a banking application? If this is typical code this whole thing is a rounding error waiting to happen and destroying the whole company.

 

OK, to the actual problem: Add a check for the array bounds to the for loop:

if Round(EntryAge) < LowBoundOfArray then
  raise exception.Create(...)
if Round(RetireAge) > HighBoundOfArray then
  raise exception.Create(...)  

Where LowBoundOfArray and HighBoundOfArray are the minimum and maximum index values for the array. I don't remember if Delphi 5 already knew the Low(array) and High(array) functions. If it does, use these.

My guess would be that a rounding problem causes access to elements ouside the valid array bounds and that causes your error.

Edited by dummzeuch
  • Like 3

Share this post


Link to post
6 hours ago, Cristian Peța said:

Is EntryAge and RetireAge floating-point?

They are defined as 'Double' inside of an object, so yes they are floating point.  If I had done the original code, I would have used integers.

 

Share this post


Link to post
6 hours ago, dummzeuch said:

WTF? Who stores ages as floating point and then uses arrays (which of course use integer indexing) for lookups? And this is a banking application? If this is typical code this whole thing is a rounding error waiting to happen and destroying the whole company.

 

OK, to the actual problem: Add a check for the array bounds to the for loop:


if Round(EntryAge) < LowBoundOfArray then
  raise exception.Create(...)
if Round(RetireAge) > HighBoundOfArray then
  raise exception.Create(...)  

Where LowBoundOfArray and HighBoundOfArray are the minimum and maximum index values for the array. I don't remember if Delphi 5 already knew the Low(array) and High(array) functions. If it does, use these.

My guess would be that a rounding problem causes access to elements ouside the valid array bounds and that causes your error.

Thanks DummZeuch, that is what I suspected and have started modifying the code as you suggested.  The routine contains a lot of code so it is going to take a while. As I mentioned, the original code was written around 1998 (using Delphi 1, believe it or not) and back in 2014 I was contracted to convert the application so something that would compile in W7.  I had to replace all of the ReportSmith reports but unfortunately, for one big key report they had used a reporting product called formGenWin which went out of business around 2006.  I obtained as much of the source code of the product that was available, the latest in D5.  The bank in their wisdom didn't want to spend the time and money to have me re-write the old report to allow later versions of Delphi.  However, the code seemed to work without any exceptions until this t time (under MS 7, done on two years cycles) so I suspected an MS update to have something to do with this problem.  This problem came up under MS7, I believe, but I am running MS10 on my machine.  I will have to check with them to see what O/S they were using when this blowup occurred.  Hopefully, I can get the error to disappear by doctoring the code as you suggest.

Share this post


Link to post
Guest

I suspect the real thing here is that maybe you used a more modern RTL or different project settings so the potential bug became an exception. Turn checking on in debug builds!

 

/D

Share this post


Link to post

Random EInvalidOp.... If you can use Jcl, then using Jcl8087 unit you could try adding

    Set8087ControlWord(Default8087CW);
    ClearPending8087Exceptions;

before the location, where error appears. For us it helped work around about some buggy printer drivers.

Also, if you happen to be using FastMM4 memory manager in FullDebugMode, then we have had random EInvalidOps with RawStackTraces on.

Share this post


Link to post
11 hours ago, Virgo said:

Random EInvalidOp.... If you can use Jcl, then using Jcl8087 unit you could try adding

    Set8087ControlWord(Default8087CW);
    ClearPending8087Exceptions;

before the location, where error appears. For us it helped work around about some buggy printer drivers.

Also, if you happen to be using FastMM4 memory manager in FullDebugMode, then we have had random EInvalidOps with RawStackTraces on.

Thanks, I'll give that a try.

Mark

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×