mjwallin47 0 Posted February 21, 2019 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
Primož Gabrijelčič 223 Posted February 21, 2019 That behavior is correct. `for` loop will execute once with `i` being set to 0. Share this post Link to post
Markus Kinzler 174 Posted February 21, 2019 FOR seams to be implemented as an exit condition loop Share this post Link to post
sakura 45 Posted February 21, 2019 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
mjwallin47 0 Posted February 21, 2019 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
Primož Gabrijelčič 223 Posted February 21, 2019 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
mjwallin47 0 Posted February 21, 2019 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
mjwallin47 0 Posted February 21, 2019 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
Cristian Peța 103 Posted February 21, 2019 Is EntryAge and RetireAge floating-point? Share this post Link to post
dummzeuch 1505 Posted February 21, 2019 (edited) 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 February 21, 2019 by dummzeuch 3 Share this post Link to post
Attila Kovacs 629 Posted February 21, 2019 @dummzeuch <joke> It's pretty fine to use Delphi's built in Round in a banking application. </joke> Share this post Link to post
Primož Gabrijelčič 223 Posted February 21, 2019 It uses "banker's rounding", after all. 😉 2 Share this post Link to post
mjwallin47 0 Posted February 21, 2019 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
mjwallin47 0 Posted February 21, 2019 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 Posted February 21, 2019 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
Virgo 18 Posted February 22, 2019 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
mjwallin47 0 Posted February 22, 2019 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