I am trying to translate an old FORTRAN program to C++ and appear to have hit a brick wall.
I do not understand how one of the variables returned by a sub-routine can ever take a particular value.
I am not a FORTRAN master; maybe somebody here is more familiar with it.

Say . . .
Subroutine1 calls Subroutine 2, and
Subroutine2 calls Subroutine 3.

Is there any way for program execution to jump from Subroutine 3 directly back to Subroutine 1 without returning to Subroutine 2 first?

Can you post the code? I spent a couple of decades programming in FORTRAN. Depending on the dialect and how much I remember I might be able to help.

Edited 1 Month Ago by Reverend Jim

Hi, Jim.

Thanks for the offer.

I am trying to translate the code for UNCMND, which does multi-dimensional optimization.
I believe the most up-to-date, complete, version of the source code is posted on the GAMS website: http://gams.nist.gov/cgi-bin/serve.cgi/Module/NMS/UNCMND/5673/

I have attached two PDF documents of the code for the sub-routines (UNCMND and OPTDRD) relevant to my question.

The main routine, UNCMND, uses the integer variable INFO as an error code to indicate the quality of the result. It can (supposedly) return a value of 0 to indicate an optimal solution.

UNCMND passes INFO into sub-routine OPTDRD, where it is referenced as the variable ITRMCD.

OPTDRD does the bulk of the work of computing the optimization.
Inside OPTDRD, I have highlighted the start and end points of the main loop in red. There is the label at 100, and the unconditional GO TO 100 statement.
Within this loop, another sub-routine is called, and the value of ITRMCD may be changed; however, the only way to break out of this loop is if ITRMCD takes on a non-zero value, which I have highlighted in purple.
Once that loop is broken, the routine finishes and program execution returns to UNCMND.
However, at no time is the value of ITRMCD (i.e., INFO) ever set back to 0.

I am baffled.
It is a Catch-22.
If ITRMCD, remains 0, it would not break out of the loop in OPTDRD.
But if it takes a non-zero value in OPTDRD to break out of the loop, INFO can never be zero in UNCMND.

As far as I understand this code, there is no way for INFO to ever have a 0 value when this program is run.

What am I missing?

Any help is much appreciated.

Edited 1 Month Ago by DavidB

OPTDRD calls OPTSTD and in OPTSTD the value of ITRMCD is set to a non-zero value at

600 ITRMCD=JTRMCD

Edited 1 Month Ago by Reverend Jim: n/a

That is some massively butt-ugly code. I hope you have a large amount of test data that you can run through both the FORTRAN and C++ versions so that you can verify your translation.

My advice is to not try and do a direct translation from Fortran to C++, but to first analyze what the code does, and then re-implement that in C++. There are tools, such as Enterprise Architect that can reverse engineer code into UML models, state machines, etc. which can help with this process.

analyze what the code does, and then re-implement that in C++

Have you seen the code? If someone handed it to you and said, "translate it" you would likely run screaming. I suspect this was written in the dark ages by an engineer (I've never known any programmer to use the IMPLICIT directive). And never mind the code, just look at this gem for initializing so-called constants.

      DOUBLE PRECISION FUNCTION D1MACH(I)
C***BEGIN PROLOGUE  D1MACH
C***DATE WRITTEN   750101   (YYMMDD)
C***REVISION DATE  831014   (YYMMDD)
C***CATEGORY NO.  R1
C***KEYWORDS  MACHINE CONSTANTS
C***AUTHOR  FOX, P. A., (BELL LABS)
C           HALL, A. D., (BELL LABS)
C           SCHRYER, N. L., (BELL LABS)
C***PURPOSE  Returns double precision machine dependent constants
C***DESCRIPTION
C     From the book, "Numerical Methods and Software" by
C                D. Kahaner, C. Moler, S. Nash
C                Prentice Hall, 1988
C
C
C     D1MACH can be used to obtain machine-dependent parameters
C     for the local machine environment.  It is a function
C     subprogram with one (input) argument, and can be called
C     as follows, for example
C
C          D = D1MACH(I)
C
C     where I=1,...,5.  The (output) value of D above is
C     determined by the (input) value of I.  The results for
C     various values of I are discussed below.
C
C  Double-precision machine constants
C  D1MACH( 1) = B**(EMIN-1), the smallest positive magnitude.
C  D1MACH( 2) = B**EMAX*(1 - B**(-T)), the largest magnitude.
C  D1MACH( 3) = B**(-T), the smallest relative spacing.
C  D1MACH( 4) = B**(1-T), the largest relative spacing.
C  D1MACH( 5) = LOG10(B)
C***REFERENCES  FOX P.A., HALL A.D., SCHRYER N.L.,*FRAMEWORK FOR A
C                 PORTABLE LIBRARY*, ACM TRANSACTIONS ON MATHEMATICAL
C                 SOFTWARE, VOL. 4, NO. 2, JUNE 1978, PP. 177-188.
C***ROUTINES CALLED  XERROR
C***END PROLOGUE  D1MACH
C
      INTEGER SMALL(4)
      INTEGER LARGE(4)
      INTEGER RIGHT(4)
      INTEGER DIVER(4)
      INTEGER LOG10(4)
C
      DOUBLE PRECISION DMACH(5)
C
      EQUIVALENCE (DMACH(1),SMALL(1))
      EQUIVALENCE (DMACH(2),LARGE(1))
      EQUIVALENCE (DMACH(3),RIGHT(1))
      EQUIVALENCE (DMACH(4),DIVER(1))
      EQUIVALENCE (DMACH(5),LOG10(1))
C
C
C     MACHINE CONSTANTS FOR THE CDC CYBER 170 SERIES (FTN5).
C
C      DATA SMALL(1) / O"00604000000000000000" /
C      DATA SMALL(2) / O"00000000000000000000" /
C
C      DATA LARGE(1) / O"37767777777777777777" /
C      DATA LARGE(2) / O"37167777777777777777" /
C
C      DATA RIGHT(1) / O"15604000000000000000" /
C      DATA RIGHT(2) / O"15000000000000000000" /
C
C      DATA DIVER(1) / O"15614000000000000000" /
C      DATA DIVER(2) / O"15010000000000000000" /
C
C      DATA LOG10(1) / O"17164642023241175717" /
C      DATA LOG10(2) / O"16367571421742254654" /
C
C     MACHINE CONSTANTS FOR THE CDC CYBER 200 SERIES
C
C     DATA SMALL(1) / X'9000400000000000' /
C     DATA SMALL(2) / X'8FD1000000000000' /
C
C     DATA LARGE(1) / X'6FFF7FFFFFFFFFFF' /
C     DATA LARGE(2) / X'6FD07FFFFFFFFFFF' /
C
C     DATA RIGHT(1) / X'FF74400000000000' /
C     DATA RIGHT(2) / X'FF45000000000000' /
C
C     DATA DIVER(1) / X'FF75400000000000' /
C     DATA DIVER(2) / X'FF46000000000000' /
C
C     DATA LOG10(1) / X'FFD04D104D427DE7' /
C     DATA LOG10(2) / X'FFA17DE623E2566A' /
C
C
C     MACHINE CONSTANTS FOR THE CDC 6000/7000 SERIES.
C
C     DATA SMALL(1) / 00564000000000000000B /
C     DATA SMALL(2) / 00000000000000000000B /
C
C     DATA LARGE(1) / 37757777777777777777B /
C     DATA LARGE(2) / 37157777777777777777B /
C
C     DATA RIGHT(1) / 15624000000000000000B /
C     DATA RIGHT(2) / 00000000000000000000B /
C
C     DATA DIVER(1) / 15634000000000000000B /
C     DATA DIVER(2) / 00000000000000000000B /
C
C     DATA LOG10(1) / 17164642023241175717B /
C     DATA LOG10(2) / 16367571421742254654B /
C
C     MACHINE CONSTANTS FOR THE CRAY 1
C
C     DATA SMALL(1) / 201354000000000000000B /
C     DATA SMALL(2) / 000000000000000000000B /
C
C     DATA LARGE(1) / 577767777777777777777B /
C     DATA LARGE(2) / 000007777777777777774B /
C
C     DATA RIGHT(1) / 376434000000000000000B /
C     DATA RIGHT(2) / 000000000000000000000B /
C
C     DATA DIVER(1) / 376444000000000000000B /
C     DATA DIVER(2) / 000000000000000000000B /
C
C     DATA LOG10(1) / 377774642023241175717B /
C     DATA LOG10(2) / 000007571421742254654B /
C
C
C     MACHINE CONSTANTS FOR THE IBM 360/370 SERIES,
C     THE XEROX SIGMA 5/7/9, THE SEL SYSTEMS 85/86, AND
C     THE PERKIN ELMER (INTERDATA) 7/32.
C
C     DATA SMALL(1),SMALL(2) / Z00100000, Z00000000 /
C     DATA LARGE(1),LARGE(2) / Z7FFFFFFF, ZFFFFFFFF /
C     DATA RIGHT(1),RIGHT(2) / Z33100000, Z00000000 /
C     DATA DIVER(1),DIVER(2) / Z34100000, Z00000000 /
C     DATA LOG10(1),LOG10(2) / Z41134413, Z509F79FF /
C
C     MACHINE CONSTATNS FOR THE IBM PC FAMILY (D. KAHANER NBS)
C
C      DATA DMACH/2.23D-308,1.79D+308,1.11D-16,2.22D-16,
C     *  0.301029995663981195D0/
C
C     MACHINE CONSTANTS FOR THE PDP-10 (KA PROCESSOR).
C
C     DATA SMALL(1),SMALL(2) / "033400000000, "000000000000 /
C     DATA LARGE(1),LARGE(2) / "377777777777, "344777777777 /
C     DATA RIGHT(1),RIGHT(2) / "113400000000, "000000000000 /
C     DATA DIVER(1),DIVER(2) / "114400000000, "000000000000 /
C     DATA LOG10(1),LOG10(2) / "177464202324, "144117571776 /
C
C     MACHINE CONSTANTS FOR THE PDP-10 (KI PROCESSOR).
C
C     DATA SMALL(1),SMALL(2) / "000400000000, "000000000000 /
C     DATA LARGE(1),LARGE(2) / "377777777777, "377777777777 /
C     DATA RIGHT(1),RIGHT(2) / "103400000000, "000000000000 /
C     DATA DIVER(1),DIVER(2) / "104400000000, "000000000000 /
C     DATA LOG10(1),LOG10(2) / "177464202324, "476747767461 /
C
C
C     MACHINE CONSTANTS FOR THE SUN-3 (INCLUDES THOSE WITH 68881 CHIP,
C       OR WITH FPA BOARD. ALSO INCLUDES SUN-2 WITH SKY BOARD. MAY ALSO
C       WORK WITH SOFTWARE FLOATING POINT ON EITHER SYSTEM.)
C
      DATA SMALL(1),SMALL(2) / X'00100000', X'00000000' /
      DATA LARGE(1),LARGE(2) / X'7FEFFFFF', X'FFFFFFFF' /
      DATA RIGHT(1),RIGHT(2) / X'3CA00000', X'00000000' /
      DATA DIVER(1),DIVER(2) / X'3CB00000', X'00000000' /
      DATA LOG10(1),LOG10(2) / X'3FD34413', X'509F79FF' /
C
C
C     MACHINE CONSTANTS FOR VAX 11/780
C     (EXPRESSED IN INTEGER AND HEXADECIMAL)
C    *** THE INTEGER FORMAT SHOULD BE OK FOR UNIX SYSTEMS***
C
C     DATA SMALL(1), SMALL(2) /        128,           0 /
C     DATA LARGE(1), LARGE(2) /     -32769,          -1 /
C     DATA RIGHT(1), RIGHT(2) /       9344,           0 /
C     DATA DIVER(1), DIVER(2) /       9472,           0 /
C     DATA LOG10(1), LOG10(2) /  546979738,  -805796613 /
C
C    ***THE HEX FORMAT BELOW MAY NOT BE SUITABLE FOR UNIX SYSYEMS***
C     DATA SMALL(1), SMALL(2) / Z00000080, Z00000000 /
C     DATA LARGE(1), LARGE(2) / ZFFFF7FFF, ZFFFFFFFF /
C     DATA RIGHT(1), RIGHT(2) / Z00002480, Z00000000 /
C     DATA DIVER(1), DIVER(2) / Z00002500, Z00000000 /
C     DATA LOG10(1), LOG10(2) / Z209A3F9A, ZCFF884FB /
C
C   MACHINE CONSTANTS FOR VAX 11/780 (G-FLOATING)
C     (EXPRESSED IN INTEGER AND HEXADECIMAL)
C    *** THE INTEGER FORMAT SHOULD BE OK FOR UNIX SYSTEMS***
C
C     DATA SMALL(1), SMALL(2) /         16,           0 /
C     DATA LARGE(1), LARGE(2) /     -32769,          -1 /
C     DATA RIGHT(1), RIGHT(2) /      15552,           0 /
C     DATA DIVER(1), DIVER(2) /      15568,           0 /
C     DATA LOG10(1), LOG10(2) /  1142112243, 2046775455 /
C
C    ***THE HEX FORMAT BELOW MAY NOT BE SUITABLE FOR UNIX SYSYEMS***
C     DATA SMALL(1), SMALL(2) / Z00000010, Z00000000 /
C     DATA LARGE(1), LARGE(2) / ZFFFF7FFF, ZFFFFFFFF /
C     DATA RIGHT(1), RIGHT(2) / Z00003CC0, Z00000000 /
C     DATA DIVER(1), DIVER(2) / Z00003CD0, Z00000000 /
C     DATA LOG10(1), LOG10(2) / Z44133FF3, Z79FF509F /
C
C
C***FIRST EXECUTABLE STATEMENT  D1MACH
      IF (I .LT. 1  .OR.  I .GT. 5)
     1   CALL XERROR( 'D1MACH -- I OUT OF BOUNDS',25,1,2)
C
      D1MACH = DMACH(I)
      RETURN
C
      END

Edited 1 Month Ago by Reverend Jim

My mistake. Apparently Stephen G. Nash, "graduated in June 1982 from Stanford University, with a Ph.D. in Computer Science."

Comments
Believe it or not, I have code from the 70's and it is cringe worthy.

Hi, Reverend Jim.

Thanks for looking at this code.

OPTDRD calls OPTSTD and in OPTSTD the value of ITRMCD is set to a non-zero value at
600 ITRMCD=JTRMCD

Okay, I see that.
Then, back in OPTDRD, ITRMCD has a non-zero value and it can break out of the loop and ultimately end the program with INFO having a non-zero value.

But how can the program ever end with INFO having a 0 value?
That is what is puzzling me; INFO = 0 is one of the valid error codes listed in the main program, but I don't see how it can ever happen.

I hope you have a large amount of test data that you can run through both the FORTRAN ...

I do not have a FORTRAN compiler and don't know where I could find one. I have heard about a free WATCOM compiler, but haven't gone to look for it yet. And I know next to nothing about it. I was hoping to avoid installing another piece of software, learning about it, debugging it, and taking a massive detour, to get back to where I am right now.

I am also considering running the code through f2c, to convert it to C code.
But f2c does a literal translation, including GO TO statements. I might end up looking at the exact same program, just in C syntax, so am not sure if anything would be gained.

In any event, I'd still have to create many test cases to run, hoping one of them would result with an INFO = 0 value.

Too bad there isn't a test suite that could do this (i.e., check branches and confirm that a variable can ever end with a specified result.)

Thanks for all the advice.

INFO == ITRMCD

ITRMCD is set to zero at the start of OPTSTD. If it never gets another value then it returns ITRMCD to OPTDRD and OPTDRD can return that value to the calling code where the parameter ITRMCD corresponds to the parameter INFO.

I can't see any way around having to install a F77 compiler, however, there is a free one available that does not require an installation. You can run it from where you extracted the files. It can be downloaded here. With a program of this complexity you will have to run the same test data through the original (FORTRAN) version and the converted (C++) version in order to verify that you get the same results. Let me know how it goes. Feel free to post follow up questions and I'll answer them if I can.

Edited 1 Month Ago by Reverend Jim

Hi, Reverend Jim.

Thanks again for the feedback.

ITRMCD is set to zero at the start of OPTSTD. If it never gets another value then it returns ITRMCD to OPTDRD and OPTDRD can return that value to the calling code where the parameter ITRMCD corresponds to the parameter INFO.

Okay. One more question before I go away, sit down, and try to finally crack this problem.

Let's say OPTSTD does not change the value of ITRMCD; ITRMCD was initialized to zero and does not change.
Program execution returns to OPTDRD.

However, the only way to break out of the main loop is if ITRMCD is not 0:

   CALL OPTSTD(N,XPLS,FPLS,GPLS,X,ITNCNT,ICSCMX,
+ ITRMCD,GRADTL,STEPTL,SX,FSCALE,ITNLIM,IRETCD,MXTAKE,
+ IPR,MSG)
   IF(ITRMCD.NE.0) GO TO 690

Otherwise, I don't see any way of getting past the GO TO 100 statement later:

GO TO 100
690

This GO TO statement is unconditional and goes back to the beginning of the main loop.
The only way to break out of this infinite loop is for ITRMCD to take on a non-zero value.
But then when execution returns to the calling sub-routine, INFO would also take on a non-zero number. It can never return with a 0 value.

With the length of the code and the high numbers of GOTOs and multiple RETURNS (all of which lead me to believe that they didn't know shit about teaching comp sci at Stanford in the 70s & 80s) I would have to print out the code blocks on a long fanfold type printer and start drawing lines in order to make sense of that dog's breakfast of code. I'm afraid I do not have the resources or time to do that.

By the way, if you download and use the York free FORTRAN compiler I suggested I should point out that the command file to compile/link (f2exe.bat) by default uses the newer (99) free form input

g77 -ffree-form %1.for -o%1.exe ..\mine\*.o %LIBRARY_PATH%\..\..\SLATEC\lib\libSLATEC.a

In order to compile/link the code you are working with you must modify that file or make a new one that runs

g77 %1.for -o%1.exe ..\mine\*.o %LIBRARY_PATH%\..\..\SLATEC\lib\libSLATEC.a

I edited UNCMND.FOR and added the following at the top

PROGRAM MAIN
STOP
END

just so that it would be a complete (if useless) program file. I wanted to see if the code would actually compile error-free under the York compiler. It did.

Assuming you are running some version of Windoze, then you can also install Cygwin and the GNU F77 compiler suite. Works quite well and it works with gdb, the GNU debugger. FWIW, I have had to reverse-engineer worse Fortran code than this. It will take some thinking "outside of the box" to get a well-structured C++ program out of it though.

The article starter has earned a lot of community kudos, and such articles offer a bounty for quality replies.