Discussion:
Print statement within If-Then block changes output!!!????
(too old to reply)
watsteel
2007-03-18 14:50:28 UTC
Permalink
Hello,
I'm new to fortran, and I'm having a problem tracking a bug in my
code.

I have a pair of If-then blocks to store a minimum residual.
While debugging, I noticed that a 'Print' statement placed within the
second block would change the program's output. I've tried 'Print *',
Write(both to * and to unit=2, which was opened.), and I've changed
the location of the 'Print' statement within the block, but all to no
avail. If the 'Print' statement is in the block, the output changes.
There are two sets of output, depending on whether the 'Print'
statement exists in the block or not.

The program is only 195 lines of code. The input file if 15 lines of
code.
I will happily e-mail them to anyone who can spare the time to look
this over--I'm stumped.

I'm using GNU G77, on a Dell laptop, running XP Home.

And my forehead is getting bruised from banging it on my desk, so any
help would be greatly appreciated.

Thanks
Vic
***@hotmail.com
Dan Nagle
2007-03-18 15:05:10 UTC
Permalink
Hello,
Post by watsteel
Hello,
I'm new to fortran, and I'm having a problem tracking a bug in my
code.
I have a pair of If-then blocks to store a minimum residual.
While debugging, I noticed that a 'Print' statement placed within the
second block would change the program's output.
That's a compiler bug.
Post by watsteel
I've tried 'Print *',
Write(both to * and to unit=2, which was opened.), and I've changed
the location of the 'Print' statement within the block, but all to no
avail. If the 'Print' statement is in the block, the output changes.
There are two sets of output, depending on whether the 'Print'
statement exists in the block or not.
The program is only 195 lines of code. The input file if 15 lines of
code.
I will happily e-mail them to anyone who can spare the time to look
this over--I'm stumped.
I'm using GNU G77, on a Dell laptop, running XP Home.
g77 is old and no longer supported.
Try using g95 or gfortran. Any f95 compiler will compile f77.
Post by watsteel
And my forehead is getting bruised from banging it on my desk, so any
help would be greatly appreciated.
--
Cheers!

Dan Nagle
Purple Sage Computing Solutions, Inc.
Beliavsky
2007-03-18 15:38:08 UTC
Permalink
Post by watsteel
Hello,
Post by watsteel
Hello,
I'm new to fortran, and I'm having a problem tracking a bug in my
code.
I have a pair of If-then blocks to store a minimum residual.
While debugging, I noticed that a 'Print' statement placed within the
second block would change the program's output.
That's a compiler bug.
Probably there is a bug in watsteel's PROGRAM, which may be trying to
access an out-of-bounds array element. When a program does that,
inserting a PRINT statement can lead to strange changes in how the
program works.
Post by watsteel
g77 is old and no longer supported.
Try using g95 or gfortran. Any f95 compiler will compile f77.
Post by watsteel
And my forehead is getting bruised from banging it on my desk, so any
help would be greatly appreciated.
The first thing to do is to turn on all the debugging options of the
compiler you are using. You can type g77 -v --help to get a list of
compiler options. Try compiling and running your code with the -
fbounds-check option.

As Dan Nagle suggested, you can try another compiler, such as g95 or
gfortran, which are both free and which support Fortran 95. Why don't
you use Fortran 95, which is a more modern version of the language?
Because of features such as modules, which help to catch errors at
compile time, I bang my head less often than I used to.
Gib Bogle
2007-03-20 21:28:10 UTC
Permalink
Post by Beliavsky
Post by watsteel
Hello,
Post by watsteel
Hello,
I'm new to fortran, and I'm having a problem tracking a bug in my
code.
I have a pair of If-then blocks to store a minimum residual.
While debugging, I noticed that a 'Print' statement placed within the
second block would change the program's output.
That's a compiler bug.
Probably there is a bug in watsteel's PROGRAM, which may be trying to
access an out-of-bounds array element. When a program does that,
inserting a PRINT statement can lead to strange changes in how the
program works.
In my experience, this is almost certainly the problem.
Gib Bogle
2007-03-20 21:30:31 UTC
Permalink
I did say "almost", luckily ...
Steven G. Kargl
2007-03-18 15:45:13 UTC
Permalink
Post by watsteel
Hello,
Post by watsteel
Hello,
I'm new to fortran, and I'm having a problem tracking a bug in my
code.
I have a pair of If-then blocks to store a minimum residual.
While debugging, I noticed that a 'Print' statement placed within the
second block would change the program's output.
That's a compiler bug.
There isn't enough information to make the above conclusion.
Without seeing the code, I'd guess that it is programming
error (e.g., using an unintialized variable or an array
index out of bounds).

Since OP is using g77, he can run his code through fntchek
and use -Wall to find the bug.
--
Steve
http://troutmask.apl.washington.edu/~kargl/
Colin Watters
2007-03-18 16:24:12 UTC
Permalink
Post by watsteel
Hello,
Post by watsteel
Hello,
I'm new to fortran, and I'm having a problem tracking a bug in my
code.
I have a pair of If-then blocks to store a minimum residual.
While debugging, I noticed that a 'Print' statement placed within the
second block would change the program's output.
That's a compiler bug.
Bet you a beer it isn't.

--
Qolin

Email: my qname at domain
Domain: qomputing dot demon dot co dot uk
Post by watsteel
Post by watsteel
I've tried 'Print *',
Write(both to * and to unit=2, which was opened.), and I've changed
the location of the 'Print' statement within the block, but all to no
avail. If the 'Print' statement is in the block, the output changes.
There are two sets of output, depending on whether the 'Print'
statement exists in the block or not.
The program is only 195 lines of code. The input file if 15 lines of
code.
I will happily e-mail them to anyone who can spare the time to look
this over--I'm stumped.
I'm using GNU G77, on a Dell laptop, running XP Home.
g77 is old and no longer supported.
Try using g95 or gfortran. Any f95 compiler will compile f77.
Post by watsteel
And my forehead is getting bruised from banging it on my desk, so any
help would be greatly appreciated.
--
Cheers!
Dan Nagle
Purple Sage Computing Solutions, Inc.
Colin Watters
2007-03-18 23:04:48 UTC
Permalink
Post by Colin Watters
Post by watsteel
Hello,
Post by watsteel
Hello,
I'm new to fortran, and I'm having a problem tracking a bug in my
code.
I have a pair of If-then blocks to store a minimum residual.
While debugging, I noticed that a 'Print' statement placed within the
second block would change the program's output.
That's a compiler bug.
Bet you a beer it isn't.
...Well I've had a look at the OP's code and played around with it under
CVF, and I can't see a good reason for the symptoms he is seeing. But then
again, nither can I reproduce his problem.

The is no array over- or under- indexing going on. The only 'questionable'
aspect of the code is the use of double-precision do-loop indices. Maybe G77
has a problem with these? Feels like I'm clutching at straws.

So, Dan, I think I owe you a beer.

--
Qolin

Email: my qname at domain
Domain: qomputing dot demon dot co dot uk --
Dan Nagle
2007-03-19 00:51:10 UTC
Permalink
Hello,
Post by Colin Watters
Post by Colin Watters
Post by watsteel
Hello,
Post by watsteel
Hello,
I'm new to fortran, and I'm having a problem tracking a bug in my
code.
I have a pair of If-then blocks to store a minimum residual.
While debugging, I noticed that a 'Print' statement placed within the
second block would change the program's output.
That's a compiler bug.
Bet you a beer it isn't.
...Well I've had a look at the OP's code and played around with it under
CVF, and I can't see a good reason for the symptoms he is seeing. But then
again, nither can I reproduce his problem.
The is no array over- or under- indexing going on. The only 'questionable'
aspect of the code is the use of double-precision do-loop indices. Maybe G77
has a problem with these? Feels like I'm clutching at straws.
So, Dan, I think I owe you a beer.
I spent some time today moving my email and news from my Linux box
to my Mac. So I could use a beer. :-)

My guess that it was a compiler bug was based on the idea
that an if-block with a print statement is usually
a rather small block, and should only force a few variables
to be stored. It was hard for me to see how that changes
the ill effects of an out-of-bounds index.

OTOH, out-of-bounds is a good first guess, too.

Drat. Now I must fix the .sig file. There's no end to this. :-(
Ron Shepard
2007-03-18 16:42:35 UTC
Permalink
Post by watsteel
I have a pair of If-then blocks to store a minimum residual.
While debugging, I noticed that a 'Print' statement placed within the
second block would change the program's output.
I assume you mean that it changes the computed results.

This is typical of an out-of-bounds array reference error. It can
also be caused by an argument mismatch in a subprogram call, or
inconsistent common block definitions. Try turning on some runtime
bounds checking options in your compiler and run your f77 code
through ftnchk. I would also suggest that you use a newer compiler,
such a gfortran or a commercial f90 compiler.

$.02 -Ron Shepard
Brooks Moses
2007-03-19 01:53:11 UTC
Permalink
Post by watsteel
I have a pair of If-then blocks to store a minimum residual.
While debugging, I noticed that a 'Print' statement placed within the
second block would change the program's output. I've tried 'Print *',
Write(both to * and to unit=2, which was opened.), and I've changed
the location of the 'Print' statement within the block, but all to no
avail. If the 'Print' statement is in the block, the output changes.
There are two sets of output, depending on whether the 'Print'
statement exists in the block or not.
The program is only 195 lines of code. The input file if 15 lines of
code.
I will happily e-mail them to anyone who can spare the time to look
this over--I'm stumped.
I'm using GNU G77, on a Dell laptop, running XP Home.
And my forehead is getting bruised from banging it on my desk, so any
help would be greatly appreciated.
Presuming that Colin's conclusion that this isn't the usual sort of
out-of-bounds error causing that problem, and that it is in fact a
compiler bug, I think I've got a fair guess which exactly compiler bug
it is, because I've run into it in exactly this situation:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323.

The short version is of the bug this: The Intel floating-point system
has 80-bit floating-point registers, but has only 64-bit floating-point
storage. Thus, there are cases where, if a value is stored in memory,
its value will be slightly different (due to removing the last 16 bits)
than if its kept in a register.

How this hit me is that I had two numbers where A was supposed to be
greater than or equal to B, but when the comparison was done, one of
them had been stored in memory and the other was still in the register
where it had been calculated, and as a result A.GE.B was false.
Printing out something in the middle of this caused all of the variables
to be shunted out of registers into 64-bit memory slots, and thus made
the bug go away.

One recommended fix for this in GCC/g77/gfortran is the -ffloat-store
option, which forces all floating-point values to go through a memory
store before being used, thereby assuring that they're consistently
truncated to 64 bits. It's worth a try to see if using that flag causes
your problem to disappear. (It does slow down the program a bit, though.)

- Brooks
--
The "bmoses-nospam" address is valid; no unmunging needed.
watsteel
2007-03-20 20:28:13 UTC
Permalink
<snip>

Hello all,

Many thanks for all the comments.

I took Qolin's advice and tried compiling using gfortran, and the
little bug seems to have been squashed. So that's good. He also
suggested I re-write the code to get rid to the Real-type DO loop
indexes, and I will do that for sure.

As a note to other comments, when I compile using G77, I use 'g77 -
Wall name.f -O -o name', but no message re this error/bug.
I have read of, but not used ftnchk. That will change.
I did 'g77 --help', and didn't spend enough time trying to find out
what all the options did. Yeah, rtfm...That too will change.

And possibly my biggest brain-fart--I assumed that while f77 was less
feature-rich than newer options such as f95, it would be stable since
it had been around for so long. Maybe I should have banged my forehead
on the desk a little earlier.

I just tried Brooks' suggestion of compiling with the -ffloat-store
switch, and hey, no buggy output, it seems.

You guys are the best!

I'll follow this post with the code, and the input file, in case
anyone else what's to have a look.

Cheers,

Vic
watsteel
2007-03-20 20:33:36 UTC
Permalink
The code:

PROGRAM MINTRES

C FORMATS

20 FORMAT(2X,1G9.4,G10.5,G10.4,G10.3,G10.5,G10.3,I5,I5 )
30 FORMAT(G10.3)
40 FORMAT(A,G10.4)
50 FORMAT(2X,
1G9.4,G10.5,G10.4,G10.3,G10.5,G10.3,I7,I7,4X,G10.3)

C DECLARATIONS:
IMPLICIT NONE

DOUBLE PRECISION CL, CRL, CNL, ChL
DOUBLE PRECISION CU, CRU, CNU, ChU
DOUBLE PRECISION CS, CRS, CNS, ChS
DOUBLE PRECISION iC, iCR, iCN, iCh
DOUBLE PRECISION iCf, iCRf, iCNf, iChf
DOUBLE PRECISION msRes, sRes, Pn

INTEGER I, J, CNT

DOUBLE PRECISION Fy(150), t(150), Pt(150)
DOUBLE PRECISION R(150), N(150), h(150)

C INITIALISE VARIABLES

DATA CL/0.0D0/, CRL/1.0D-4/, CNL/1.0D-4/, ChL/1.0D-4/
DATA CU/2.0D1/, CRU/1000.1D-3/, CNU/1000.1D-3/, ChU/1000.1D-3/
DATA CS/0.5D0/, CRS/2.5D-2/, CNS/2.5D-2/, ChS/2.5D-2/

DATA iC, iCR, iCN, iCh/4*0.0D0/
DATA iCf, iCRf, iCNf, iChf/4*0.0D0/
DATA msRES, sRES, Pn/3*0.0D0/
DATA I/1/, J/1/, CNT/0/

D /*debug stuff
PRINT *, CL, CRL, CNL, ChL
PRINT *, CU, CRU, CNU, ChU
PRINT *, CS, CRS, CNS, ChS
PRINT *, msRES, sRES, Pn
D debug stuff*/

OPEN(1,FILE="bdata_d.txt")

DO WHILE (.TRUE.)
READ (1,*, end=200) t(I), Fy(I), R(I), N(I), h(I), Pt(I)

WRITE(*,20) t(I), Fy(I), R(I), N(I), h(I), Pt(I), CNT, I
CNT = CNT + 1
I = I + 1
END DO
200 CONTINUE

CLOSE(1)

DO 540 iC = CL, CU, CS
DO 530 iCR = CRL, CRU, CRS
DO 520 iCN = CNL, CNU, CNS
DO 510 iCh = ChL, ChU, ChS
sRES = 0.0D0
DO 500 I = 1, CNT

Pn=iC*(t(I)**2)*(Fy(I)/1.d3)*(1-iCR*SQRT(R(I)/t(I)))*
& (1+iCN*SQRT(N(I)/t(I)))*(1-iCh*SQRT(h(I)/t(I)))

D print *, sRES, Pn, Pt(I)

sRES = sRES + ((Pn - Pt(I))**2)


500 CONTINUE
D print *, sRES
C Store the first one

IF(J.EQ.1) THEN
msRES = sRES
iCf = iC
iCRf = iCR
iCNf = iCN
iChf = iCh
J = 2

END IF

C Change msRES if the new one is smaller

IF(sRES.LT.msRES) THEN
D This is the block of interest, it seems
D Any of these PRINT or WRITE statements, when un-commented
D causes a change in the final co-efficient values
D print *
D write (*,3000) sRES, iC, iCR, iCN, iCh, J, ' 1st'
D write (*,3000) msRES, iCf, iCRf, iCNf, iChf, J, '
2nd'
msRES = sRES
iCf = iC
iCRf = iCR
iCNf = iCN
iChf = iCh

d write (*,3000) sRES, iC, iCR, iCN, iCh, J, ' 3rd'
D write (*,3000) msRES, iCf, iCRf, iCNf, iChf, J, '
4th'
3000 format (5(G10.4),I6, A)

END IF
c write (*,3000) sRES, iC, iCR, iCN, iCh, J, ' 5th'
c write (*,3000) msRES, iCf, iCRf, iCNf, iChf, J, '
6th'

510 CONTINUE
520 CONTINUE
530 CONTINUE
540 CONTINUE


C Output the final coefficients and the original data from
C "BDATA.TXT"
OPEN(2,FILE="bout.txt",STATUS='NEW', ERR=1000)

WRITE(2,2004)
WRITE(2,2005)
2004 FORMAT(3X,'t(I)',5X,'Fy(I)',6X,'R(I)',6X,'N(I)',5X,'h(I)',7X,
& 'Pt(I)',8X,'CNT',5X,'I',7X ,'Pn')
2005 FORMAT(91('-'))

C Calculate Pn
DO 600 I = 1, CNT

Pn =iCf*(t(I)**2)*(Fy(I)/1.d3)*(1-iCRf*SQRT(R(I)/t(I)))*
& (1+iCNf*SQRT(N(I)/t(I)))*(1-iChf*SQRT(h(I)/t(I)))

WRITE(2,50) t(I), Fy(I), R(I), N(I), h(I), Pt(I), CNT, I , Pn

D write (*,3000) msRES, iCf, iCRf, iCNf, iChf, J, ' pEnd'

600 CONTINUE

D write (*,3000) msRES, iCf, iCRf, iCNf, iChf, J, ' End'

WRITE(2,40)'iCf = ', iCf
WRITE(2,40)'iCRf = ',iCRf
WRITE(2,40)'iCNf = ',iCNf
WRITE(2,40)'iChf = ',iChf

WRITE(2,*)'iCf = ', iCf
WRITE(2,*)'iCRf = ',iCRf
WRITE(2,*)'iCNf = ',iCNf
WRITE(2,*)'iChf = ',iChf

CLOSE(2)

GOTO 1100

1000 PRINT*,"OUTPUT FILE EXISTS!"

1100 STOP

END


And the data file bdata_d.txt
0.791D0 335.58D0 3.367767857D0 25.D0 83.68246429D0 1.857D0
0.791D0 335.58D0 3.367767857D0 25.D0 83.68246429D0 2.1235D0
1.078D0 307.72D0 3.175D0 25.D0 83.494D0 4.036D0
1.078D0 307.72D0 3.175D0 25.D0 83.494D0 3.57D0
0.791D0 335.58D0 3.367767857D0 25.D0 66.19282925D0 2.11D0
0.791D0 335.58D0 3.367767857D0 25.D0 66.19282925D0 2.16D0
1.078D0 307.72D0 3.175D0 25.D0 90.006532D0 4.38D0
1.078D0 307.72D0 3.175D0 25.D0 90.006532D0 3.955D0
1.547D0 391.01D0 3.186024306D0 25.D0 127.6800228D0 7.315315D0
1.547D0 391.01D0 3.186024306D0 25.D0 127.6800228D0 7.09741D0
1.874D0 453.8D0 3.19484375D0 25.D0 153.4099736D0 5.74746D0
1.874D0 453.8D0 3.19484375D0 25.D0 153.4099736D0 6.463815D0
1.078D0 307.72D0 3.175D0 25.D0 90.006532D0 4.46D0
1.078D0 307.72D0 3.175D0 25.D0 90.006532D0 4.245D0
0.791D0 335.58D0 3.367767857D0 25.D0 66.19282925D0 2.204D0

Cheers,

Vic
Beliavsky
2007-03-20 20:47:19 UTC
Permalink
Would you mind if I made some constructive stylistic comments on the
code?
watsteel
2007-03-21 00:17:45 UTC
Permalink
Post by Beliavsky
Would you mind if I made some constructive stylistic comments on the
code?
I'd appreciate any constructive comments, stylistic or otherwise.

Thanks,
Beliavsky
2007-03-21 02:03:56 UTC
Permalink
On Mar 20, 3:33 pm, "watsteel" <***@gmail.com> wrote:

<snip>

"Magic numbers" are bad. Write

INTEGER N
PARAMETER (N = 150)

and use N in the declarations below.

In Fortran 90 you can write

INTEGER, PARAMETER :: N = 150
Post by watsteel
DOUBLE PRECISION Fy(150), t(150), Pt(150)
DOUBLE PRECISION R(150), N(150), h(150)
<snip>
Post by watsteel
OPEN(1,FILE="bdata_d.txt")
One should specify the ACTION in an OPEN statement and use a PARAMETER
for file units. You could write

OPEN(in_unit,file="bdata_d.txt",action="read",status="old")

where in_unit would be a PARAMETER defined earlier.
Post by watsteel
DO WHILE (.TRUE.)
Instead of the line above, just write "DO"
Post by watsteel
READ (1,*, end=200) t(I), Fy(I), R(I), N(I), h(I), Pt(I)
WRITE(*,20) t(I), Fy(I), R(I), N(I), h(I), Pt(I), CNT, I
CNT = CNT + 1
I = I + 1
END DO
<snip>
Post by watsteel
510 CONTINUE
520 CONTINUE
530 CONTINUE
540 CONTINUE
Use ENDDO. A compiler supporting DO WHILE will support ENDDO.

<snip>
Jugoslav Dujic
2007-03-22 12:39:46 UTC
Permalink
Beliavsky wrote:
| On Mar 20, 3:33 pm, "watsteel" <***@gmail.com> wrote:
|| DO WHILE (.TRUE.)
| Instead of the line above, just write "DO"

On the same stylistic nitpicking wavelength, I prefer the original
verbose version. It's slightly more obvious, but, more importantly,
it's far easier to grep your sources for "while (.true.)" once
your 100,000 lines program gets stuck in an unknown location. Not
that it ever happened to me... :o)
--
Jugoslav
___________
www.xeffort.com

Please reply to the newsgroup.
You can find my real e-mail on my home page above.
FX
2007-03-22 13:09:09 UTC
Permalink
Post by Jugoslav Dujic
it's far easier to grep your sources for "while (.true.)"
Far easier than what? Than grepping for "do\s*$"?
--
FX
Dan Nagle
2007-03-22 13:17:41 UTC
Permalink
Hello,
Post by Jugoslav Dujic
|| DO WHILE (.TRUE.)
| Instead of the line above, just write "DO"
On the same stylistic nitpicking wavelength, I prefer the original
verbose version. It's slightly more obvious, but, more importantly,
it's far easier to grep your sources for "while (.true.)" once
your 100,000 lines program gets stuck in an unknown location. Not
that it ever happened to me... :o)
If grepping is a concern, try using a label for the block.
--
Dan Nagle
Purple Sage Computing Solutions, Inc.
Richard Maine
2007-03-22 16:12:32 UTC
Permalink
Post by Dan Nagle
Post by Jugoslav Dujic
|| DO WHILE (.TRUE.)
| Instead of the line above, just write "DO"
On the same stylistic nitpicking wavelength, I prefer the original
verbose version. It's slightly more obvious, but, more importantly,
it's far easier to grep your sources for "while (.true.)" once
your 100,000 lines program gets stuck in an unknown location. Not
that it ever happened to me... :o)
If grepping is a concern, try using a label for the block.
Or even putting a comment on the line.

As for "obvious", well I guess that's in the eye of the beholder. I
suspect you are just used to the practice and so recognize it. I find it
cryptic, about along the line of simillar do-nothing constructs I've
seen such as

x = x

and

if (something) continue

or

goto 100
100 continue

(Note the lack of ellipses in the last one. Nothing between those
lines). There can be valid reasons for all these constructs. I've even
written at least the last two. But they confuse readers into wondering
whether they are actually supposed to do something or not.
--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain
e p chandler
2007-03-22 17:19:52 UTC
Permalink
Post by Richard Maine
Post by Dan Nagle
Post by Jugoslav Dujic
|| DO WHILE (.TRUE.)
| Instead of the line above, just write "DO"
On the same stylistic nitpicking wavelength, I prefer the original
verbose version. It's slightly more obvious, but, more importantly,
it's far easier to grep your sources for "while (.true.)" once
your 100,000 lines program gets stuck in an unknown location. Not
that it ever happened to me... :o)
If grepping is a concern, try using a label for the block.
Or even putting a comment on the line.
As for "obvious", well I guess that's in the eye of the beholder. I
suspect you are just used to the practice and so recognize it. I find it
cryptic, about along the line of simillar do-nothing constructs I've
seen such as
x = x
and
if (something) continue
or
goto 100
100 continue
(Note the lack of ellipses in the last one. Nothing between those
lines). There can be valid reasons for all these constructs. I've even
written at least the last two. But they confuse readers into wondering
whether they are actually supposed to do something or not.
--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain
Thanks for giving me an excuse to dig out some old and nasty code that
does *exactly* what you describe. Bear in mind that I wrote it just
for fun as an exercise in using old computer hardware and software. I
would *never* (well, hardly ever) do something as crufty as this in
real production code. :-). Or as James Van Buskirk might say, it's
about knowing what you want the object code to be and fooling your
assembler/compiler into producing it.

I'm using Nevada Fortran on CP/M in these programs. This old and
strange compiler compiled to 8080 code. It did all calculations in its
own form of Binary Coded Decimal (BCD) Floating point. It's an
extended FORTRAN IV without character variables. One "feature" was the
ability to "in-line" 8080 assembly language statements. Here I want to
create a version of the ACHAR function which sets the first byte of a
string to a certain BINARY value. I can't simply manipulate an
ordinary variable because all of them are BCD floating point. I can
POKE a value into memory, once I know its address.
All variables are 48 bits (6 bytes) long.

Here is the first program:

do 20 i=65,70
t=ichr(i)
write(1,10)i,t,t
10 format(i2,1x,a1,1x,k12)
20 continue
call exit
end

function getadr(x)
x=x
continue
* shld 254
getadr=peek(254)+256*peek(255)-5
return
end

function ichr(x)
ichr=' '
call poke(getadr(ichr),x)
return
end

In function getadr(),

x=x

leaves a pointer to the last byte of x in the HL register. (Don't ask
how I found this out.:-))

continue

is required to align the generated in-line assembly code

shld 254

stuffs the address contained in HL into a known pair of memory
locations

getdr=....

converts the binary to a floating point value suitable for POKE.

Note that locations 254 and 255 are the end of the default DTA under
CP/M and are probably safe to use in this fashion.

The second program is a bit simpler:

do 20 i=65,70
t=ichr(i)
write(1,10)i,t,t
10 format(i2,1x,a1,1x,k12)
20 continue
call exit
end

function ichr(x)
ichr=' '
call poke(255,x)
call move(1,255,-1,ichr,0)
return
end

MOVE is a library routine that does a memory move either from an
absolute address or from a variable (plus offset) to a variable plus
offset. This is only slightly less obscure. :-).
K12 format dumps a variable as the text representation of memory bytes
(in hex).

So there you are. A "real" reason to put a "useless"

x=x

and

continue

into a program.

-- elliot [It's only a hobby.]
Gordon Sande
2007-03-22 17:50:23 UTC
Permalink
Post by e p chandler
I'm using Nevada Fortran on CP/M in these programs. This old and
strange compiler compiled to 8080 code. It did all calculations in its
own form of Binary Coded Decimal (BCD) Floating point.
An example of a decimal arithmetic Fortran. No need for folks to
say "it might be possible" to have other than binary (or oct or hex).
Post by e p chandler
It's an
extended FORTRAN IV without character variables.
Dick Hendrickson
2007-03-22 18:14:57 UTC
Permalink
Post by Gordon Sande
Post by e p chandler
I'm using Nevada Fortran on CP/M in these programs. This old and
strange compiler compiled to 8080 code. It did all calculations in its
own form of Binary Coded Decimal (BCD) Floating point.
An example of a decimal arithmetic Fortran. No need for folks to
say "it might be possible" to have other than binary (or oct or hex).
Also, literally as we speak, the IEEE folk are in the process of
standardizing two (count-em, two) 32 bit representations for a packed
decimal format. (As near as I can recall, at least two different
hardware vendors had at least two different favorite representations.)

Fortunately, the much maligned Fortran KIND and SELECTED_*_KIND
mechanism was easily adapted in F2008 to accommodate both of
them. Basically, tweak something minor in I/O and add a "radix"
argument (and a host of associated error returns) to the
SELECTED_*_KIND routines.

Dick Hendrickson
Post by Gordon Sande
Post by e p chandler
It's an
extended FORTRAN IV without character variables.
Lane Straatman
2007-03-22 18:17:26 UTC
Permalink
Post by e p chandler
Post by Richard Maine
Post by Dan Nagle
Post by Jugoslav Dujic
|| DO WHILE (.TRUE.)
| Instead of the line above, just write "DO"
On the same stylistic nitpicking wavelength, I prefer the original
verbose version. It's slightly more obvious, but, more importantly,
it's far easier to grep your sources for "while (.true.)" once
your 100,000 lines program gets stuck in an unknown location. Not
that it ever happened to me... :o)
If grepping is a concern, try using a label for the block.
Or even putting a comment on the line.
As for "obvious", well I guess that's in the eye of the beholder. I
suspect you are just used to the practice and so recognize it. I find it
cryptic, about along the line of simillar do-nothing constructs I've
seen such as
x = x
and
if (something) continue
or
goto 100
100 continue
(Note the lack of ellipses in the last one. Nothing between those
lines). There can be valid reasons for all these constructs. I've even
written at least the last two. But they confuse readers into wondering
whether they are actually supposed to do something or not.
Thanks for giving me an excuse to dig out some old and nasty code that
does *exactly* what you describe. Bear in mind that I wrote it just
for fun as an exercise in using old computer hardware and software. I
would *never* (well, hardly ever) do something as crufty as this in
real production code. :-). Or as James Van Buskirk might say, it's
about knowing what you want the object code to be and fooling your
assembler/compiler into producing it.
I'm using Nevada Fortran on CP/M in these programs. This old and
strange compiler compiled to 8080 code. It did all calculations in its
own form of Binary Coded Decimal (BCD) Floating point. It's an
extended FORTRAN IV without character variables. One "feature" was the
ability to "in-line" 8080 assembly language statements. Here I want to
create a version of the ACHAR function which sets the first byte of a
string to a certain BINARY value. I can't simply manipulate an
ordinary variable because all of them are BCD floating point. I can
POKE a value into memory, once I know its address.
All variables are 48 bits (6 bytes) long.
do 20 i=65,70
t=ichr(i)
write(1,10)i,t,t
10 format(i2,1x,a1,1x,k12)
20 continue
call exit
end
function getadr(x)
x=x
continue
* shld 254
getadr=peek(254)+256*peek(255)-5
return
end
function ichr(x)
ichr=' '
call poke(getadr(ichr),x)
return
end
In function getadr(),
x=x
leaves a pointer to the last byte of x in the HL register. (Don't ask
how I found this out.:-))
continue
is required to align the generated in-line assembly code
shld 254
stuffs the address contained in HL into a known pair of memory
locations
getdr=....
converts the binary to a floating point value suitable for POKE.
Note that locations 254 and 255 are the end of the default DTA under
CP/M and are probably safe to use in this fashion.
do 20 i=65,70
t=ichr(i)
write(1,10)i,t,t
10 format(i2,1x,a1,1x,k12)
20 continue
call exit
end
function ichr(x)
ichr=' '
call poke(255,x)
call move(1,255,-1,ichr,0)
return
end
MOVE is a library routine that does a memory move either from an
absolute address or from a variable (plus offset) to a variable plus
offset. This is only slightly less obscure. :-).
K12 format dumps a variable as the text representation of memory bytes
(in hex).
So there you are. A "real" reason to put a "useless"
x=x
and
continue
into a program.
I'm trying to see what you guys are getting at. When I try to compile
either of the above I get:
error 274 - Unknown edit descriptor 'K', or missing comma
for the line labelled 10. It sounds like they're supposed to compile. What
gives?
--
LS
e p chandler
2007-03-22 18:43:42 UTC
Permalink
Post by Lane Straatman
Post by e p chandler
Post by Richard Maine
Post by Dan Nagle
Post by Jugoslav Dujic
|| DO WHILE (.TRUE.)
| Instead of the line above, just write "DO"
On the same stylistic nitpicking wavelength, I prefer the original
verbose version. It's slightly more obvious, but, more importantly,
it's far easier to grep your sources for "while (.true.)" once
your 100,000 lines program gets stuck in an unknown location. Not
that it ever happened to me... :o)
If grepping is a concern, try using a label for the block.
Or even putting a comment on the line.
As for "obvious", well I guess that's in the eye of the beholder. I
suspect you are just used to the practice and so recognize it. I find it
cryptic, about along the line of simillar do-nothing constructs I've
seen such as
x = x
and
if (something) continue
or
goto 100
100 continue
(Note the lack of ellipses in the last one. Nothing between those
lines). There can be valid reasons for all these constructs. I've even
written at least the last two. But they confuse readers into wondering
whether they are actually supposed to do something or not.
Thanks for giving me an excuse to dig out some old and nasty code that
does *exactly* what you describe. Bear in mind that I wrote it just
for fun as an exercise in using old computer hardware and software. I
would *never* (well, hardly ever) do something as crufty as this in
real production code. :-). Or as James Van Buskirk might say, it's
about knowing what you want the object code to be and fooling your
assembler/compiler into producing it.
I'm using Nevada Fortran on CP/M in these programs. This old and
strange compiler compiled to 8080 code. It did all calculations in its
own form of Binary Coded Decimal (BCD) Floating point. It's an
extended FORTRAN IV without character variables. One "feature" was the
ability to "in-line" 8080 assembly language statements. Here I want to
create a version of the ACHAR function which sets the first byte of a
string to a certain BINARY value. I can't simply manipulate an
ordinary variable because all of them are BCD floating point. I can
POKE a value into memory, once I know its address.
All variables are 48 bits (6 bytes) long.
do 20 i=65,70
t=ichr(i)
write(1,10)i,t,t
10 format(i2,1x,a1,1x,k12)
20 continue
call exit
end
function getadr(x)
x=x
continue
* shld 254
getadr=peek(254)+256*peek(255)-5
return
end
function ichr(x)
ichr=' '
call poke(getadr(ichr),x)
return
end
In function getadr(),
x=x
leaves a pointer to the last byte of x in the HL register. (Don't ask
how I found this out.:-))
continue
is required to align the generated in-line assembly code
shld 254
stuffs the address contained in HL into a known pair of memory
locations
getdr=....
converts the binary to a floating point value suitable for POKE.
Note that locations 254 and 255 are the end of the default DTA under
CP/M and are probably safe to use in this fashion.
do 20 i=65,70
t=ichr(i)
write(1,10)i,t,t
10 format(i2,1x,a1,1x,k12)
20 continue
call exit
end
function ichr(x)
ichr=' '
call poke(255,x)
call move(1,255,-1,ichr,0)
return
end
MOVE is a library routine that does a memory move either from an
absolute address or from a variable (plus offset) to a variable plus
offset. This is only slightly less obscure. :-).
K12 format dumps a variable as the text representation of memory bytes
(in hex).
So there you are. A "real" reason to put a "useless"
x=x
and
continue
into a program.
I'm trying to see what you guys are getting at. When I try to compile
error 274 - Unknown edit descriptor 'K', or missing comma
for the line labelled 10. It sounds like they're supposed to compile. What
gives?
--
LS- Hide quoted text -
- Show quoted text -
the K edit descriptor is one of the non-standard features of Nevada
Fortran.

I posted the programs to illustrate seemingly "do-nothing" code. If
you want to actually compile and run them you need either a CP/M
machine (with an 8080 or Z80 processor) or CP/M emulation software
(Simeon Cran's MyZ80 is best here) plus Nevada Fortran. :-). [Perhaps
my sense of humor was not obvious enough. :-(.]

-- elliot [Running old computer software is a great hobby. It keeps me
from filling my basement with expensive model trains!]
glen herrmannsfeldt
2007-03-22 19:58:00 UTC
Permalink
e p chandler wrote:
(snip)
Post by e p chandler
I posted the programs to illustrate seemingly "do-nothing" code. If
you want to actually compile and run them you need either a CP/M
machine (with an 8080 or Z80 processor) or CP/M emulation software
(Simeon Cran's MyZ80 is best here) plus Nevada Fortran. :-). [Perhaps
my sense of humor was not obvious enough. :-(.]
-- elliot [Running old computer software is a great hobby. It keeps me
from filling my basement with expensive model trains!]
You could fill it with expensive old computers.

Real Altair 8800's are pretty expensive, other S100 bus machines
aren't quite as expensive. There are now new IMSAI machines,

http://www.imsai.net/products/imsai_series_two.htm

(Note: I don't own one, know anyone who does, know anyone
in the company producing them, etc.)

-- glen
Lane Straatman
2007-03-23 01:37:53 UTC
Permalink
Post by e p chandler
Post by Lane Straatman
Post by e p chandler
Post by Richard Maine
Post by Dan Nagle
Post by Jugoslav Dujic
|| DO WHILE (.TRUE.)
| Instead of the line above, just write "DO"
On the same stylistic nitpicking wavelength, I prefer the original
verbose version. It's slightly more obvious, but, more
importantly,
it's far easier to grep your sources for "while (.true.)" once
your 100,000 lines program gets stuck in an unknown location. Not
that it ever happened to me... :o)
If grepping is a concern, try using a label for the block.
Or even putting a comment on the line.
As for "obvious", well I guess that's in the eye of the beholder. I
suspect you are just used to the practice and so recognize it. I find it
cryptic, about along the line of simillar do-nothing constructs I've
seen such as
x = x
and
if (something) continue
or
goto 100
100 continue
(Note the lack of ellipses in the last one. Nothing between those
lines). There can be valid reasons for all these constructs. I've even
written at least the last two. But they confuse readers into wondering
whether they are actually supposed to do something or not.
Thanks for giving me an excuse to dig out some old and nasty code that
does *exactly* what you describe. Bear in mind that I wrote it just
for fun as an exercise in using old computer hardware and software. I
would *never* (well, hardly ever) do something as crufty as this in
real production code. :-). Or as James Van Buskirk might say, it's
about knowing what you want the object code to be and fooling your
assembler/compiler into producing it.
I'm using Nevada Fortran on CP/M in these programs. This old and
strange compiler compiled to 8080 code. It did all calculations in its
own form of Binary Coded Decimal (BCD) Floating point. It's an
extended FORTRAN IV without character variables. One "feature" was the
ability to "in-line" 8080 assembly language statements. Here I want to
create a version of the ACHAR function which sets the first byte of a
string to a certain BINARY value. I can't simply manipulate an
ordinary variable because all of them are BCD floating point. I can
POKE a value into memory, once I know its address.
All variables are 48 bits (6 bytes) long.
do 20 i=65,70
t=ichr(i)
write(1,10)i,t,t
10 format(i2,1x,a1,1x,k12)
20 continue
call exit
end
function getadr(x)
x=x
continue
* shld 254
getadr=peek(254)+256*peek(255)-5
return
end
function ichr(x)
ichr=' '
call poke(getadr(ichr),x)
return
end
In function getadr(),
x=x
leaves a pointer to the last byte of x in the HL register. (Don't ask
how I found this out.:-))
continue
is required to align the generated in-line assembly code
shld 254
stuffs the address contained in HL into a known pair of memory
locations
getdr=....
converts the binary to a floating point value suitable for POKE.
Note that locations 254 and 255 are the end of the default DTA under
CP/M and are probably safe to use in this fashion.
do 20 i=65,70
t=ichr(i)
write(1,10)i,t,t
10 format(i2,1x,a1,1x,k12)
20 continue
call exit
end
function ichr(x)
ichr=' '
call poke(255,x)
call move(1,255,-1,ichr,0)
return
end
MOVE is a library routine that does a memory move either from an
absolute address or from a variable (plus offset) to a variable plus
offset. This is only slightly less obscure. :-).
K12 format dumps a variable as the text representation of memory bytes
(in hex).
So there you are. A "real" reason to put a "useless"
x=x
and
continue
into a program.
I'm trying to see what you guys are getting at. When I try to compile
error 274 - Unknown edit descriptor 'K', or missing comma
for the line labelled 10. It sounds like they're supposed to compile.
What
gives?
--
LS- Hide quoted text -
- Show quoted text -
the K edit descriptor is one of the non-standard features of Nevada
Fortran.
I posted the programs to illustrate seemingly "do-nothing" code. If
you want to actually compile and run them you need either a CP/M
machine (with an 8080 or Z80 processor) or CP/M emulation software
(Simeon Cran's MyZ80 is best here) plus Nevada Fortran. :-). [Perhaps
my sense of humor was not obvious enough. :-(.]
Having a pet project on a cpm machine sounds like fun to me. I thought what
Richard was talking about was stuff that compiles but doesn't do a whole
lot. Assigning x to itself. If true. Goto the next statement. What is a
K descriptor that my compiler knows enough about it to have it in an error
message but not have it?
Post by e p chandler
-- elliot [Running old computer software is a great hobby. It keeps me
from filling my basement with expensive model trains!]
Does it have a floppy disk drive that is actually floppy?
--
LS
glen herrmannsfeldt
2007-03-23 03:13:24 UTC
Permalink
Lane Straatman wrote:

(snip)
Post by Lane Straatman
Having a pet project on a cpm machine sounds like fun to me. I thought what
Richard was talking about was stuff that compiles but doesn't do a whole
lot. Assigning x to itself. If true. Goto the next statement. What is a
K descriptor that my compiler knows enough about it to have it in an error
message but not have it?
It might be that if you put any letter in the compiler will recognize
it enough to issue an error message. It might be that some don't
check them until execution, but most check at compile time for
valid format descriptors.
Post by Lane Straatman
Post by e p chandler
-- elliot [Running old computer software is a great hobby. It keeps me
from filling my basement with expensive model trains!]
Does it have a floppy disk drive that is actually floppy?
There are emulators available for many old machines: Apple II,
Z80/CPM, many of the old game consoles, VAX/VMS, IBM S/370,
7090/IBSYS. I think there are some working on the 704, though
I am not sure about the status of the Fortran I compiler.

I have an 8 inch floppy drive, but it isn't connected to any
machine right now.

-- glen
e p chandler
2007-03-23 03:11:45 UTC
Permalink
Post by glen herrmannsfeldt
(snip)
Post by Lane Straatman
Having a pet project on a cpm machine sounds like fun to me. I thought what
Richard was talking about was stuff that compiles but doesn't do a whole
lot. Assigning x to itself. If true. Goto the next statement. What is a
K descriptor that my compiler knows enough about it to have it in an error
message but not have it?
It might be that if you put any letter in the compiler will recognize
it enough to issue an error message. It might be that some don't
check them until execution, but most check at compile time for
valid format descriptors.
Post by Lane Straatman
Post by e p chandler
-- elliot [Running old computer software is a great hobby. It keeps me
from filling my basement with expensive model trains!]
Does it have a floppy disk drive that is actually floppy?
There are emulators available for many old machines: Apple II,
Z80/CPM, many of the old game consoles, VAX/VMS, IBM S/370,
7090/IBSYS. I think there are some working on the 704, though
I am not sure about the status of the Fortran I compiler.
I have an 8 inch floppy drive, but it isn't connected to any
machine right now.
-- glen
[taking this thread OT] I usually run (pre MS-DOS) software on an
emulator rather than on real hardware. There are advantages when
playing with "antique" programs.

1. The emulator is often many times faster than the original hardware.
2. Very large (or almost unlimited) disk space.
3. File import and export from the host.
4. Ability to use the host's editing, file decompression/un-archiving
facilities, etc.
5. If the emulator runs in a window instead of full screen, I've got
mark, cut, paste, the clipboard, etc.
6. No more running NULL modem cables, firing up X-Modem for file
transfers, etc.
7. Changing "pseudo" floppy disks instead of real disks. (I would have
worn out the drive doors on my Apple II long ago...)

It's sort of fun to do things with old programs that were not possible
on the original hardware because of limited time or disk.

One emulator that old Fortraners might enjoy playing with is one for
the IBM 1130.
see http://ibm1130.org/

-- elliot
Richard Maine
2007-03-23 03:38:20 UTC
Permalink
Post by e p chandler
[taking this thread OT] I usually run (pre MS-DOS) software on an
emulator rather than on real hardware. There are advantages when
playing with "antique" programs.
I've got a real Apple 2e. Still works - or anyway it did the last time I
tried, which was within the last year. But I bought a piece of emulator
software anyway, as I'd rather run the emulator than my real machine.
Besides the reasons you mentioned, advantages of the emulator are:

1. It's handy. Right here on the machine I'm using anyway (24" iMac)
instead of having to drag the 2e out of where I have it stashed.

2. It's more reliable - particularly the floppies. No floppy read
errors.

3. I'm not putting wear on my old hardware - mostly the floppy drives
and the floppy media, both of which are definitely subject to physical
wear.
--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain
Gary Scott
2007-03-23 12:16:13 UTC
Permalink
Post by e p chandler
Post by glen herrmannsfeldt
(snip)
Post by Lane Straatman
Having a pet project on a cpm machine sounds like fun to me. I thought what
Richard was talking about was stuff that compiles but doesn't do a whole
lot. Assigning x to itself. If true. Goto the next statement. What is a
K descriptor that my compiler knows enough about it to have it in an error
message but not have it?
It might be that if you put any letter in the compiler will recognize
it enough to issue an error message. It might be that some don't
check them until execution, but most check at compile time for
valid format descriptors.
Post by Lane Straatman
Post by e p chandler
-- elliot [Running old computer software is a great hobby. It keeps me
from filling my basement with expensive model trains!]
Does it have a floppy disk drive that is actually floppy?
There are emulators available for many old machines: Apple II,
Z80/CPM, many of the old game consoles, VAX/VMS, IBM S/370,
7090/IBSYS. I think there are some working on the 704, though
I am not sure about the status of the Fortran I compiler.
I have an 8 inch floppy drive, but it isn't connected to any
machine right now.
-- glen
[taking this thread OT] I usually run (pre MS-DOS) software on an
emulator rather than on real hardware. There are advantages when
playing with "antique" programs.
1. The emulator is often many times faster than the original hardware.
2. Very large (or almost unlimited) disk space.
3. File import and export from the host.
4. Ability to use the host's editing, file decompression/un-archiving
facilities, etc.
5. If the emulator runs in a window instead of full screen, I've got
mark, cut, paste, the clipboard, etc.
6. No more running NULL modem cables, firing up X-Modem for file
transfers, etc.
7. Changing "pseudo" floppy disks instead of real disks. (I would have
worn out the drive doors on my Apple II long ago...)
It's sort of fun to do things with old programs that were not possible
on the original hardware because of limited time or disk.
One emulator that old Fortraners might enjoy playing with is one for
the IBM 1130.
see http://ibm1130.org/
I want to emulate a 32 way parallel sysplex 390 on my 2Ghz PC.
Post by e p chandler
-- elliot
--
Gary Scott
mailto:***@sbcglobal dot net

Fortran Library: http://www.fortranlib.com

Support the Original G95 Project: http://www.g95.org
-OR-
Support the GNU GFortran Project: http://gcc.gnu.org/fortran/index.html

If you want to do the impossible, don't hire an expert because he knows
it can't be done.

-- Henry Ford
glen herrmannsfeldt
2007-03-23 13:28:41 UTC
Permalink
Gary Scott wrote:

(snip)
Post by Gary Scott
I want to emulate a 32 way parallel sysplex 390 on my 2Ghz PC.
I don't believe that Hercules implements sysplex, but it
does implement multi-CPU machines up through z/Architecture.
(z/Linux being the legal OS to run on it for most people.)

The suggestion is to have as many real processors as virtual
processors, but that isn't required.

-- glen
Richard Maine
2007-03-22 18:51:59 UTC
Permalink
Post by Lane Straatman
I'm trying to see what you guys are getting at. When I try to compile
error 274 - Unknown edit descriptor 'K', or missing comma
for the line labelled 10. It sounds like they're supposed to compile. What
gives?
No, this isn't "supposed to compile." EP explained that this is *HIGHLY*
compiler specific. The odds of you happening to have the compiler that
it was written for (along with an appropriate system) are pretty much
zero. He posted it as an amusing example - not as something anyone else
would take and use as is. (And I did find it an interesting example.)
--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain
Kevin G. Rhoads
2007-03-28 15:44:00 UTC
Permalink
Post by Richard Maine
There can be valid reasons for all these constructs. I've even
written at least the last two. But they confuse readers into wondering
whether they are actually supposed to do something or not.
Isn't that one of the things comments are for?

I sometimes find myself writing things like
IF (condition) THEN
* we aren't doing anything here, yet
ELSE
...
ENDIF
because I expect to fill in the do nothing blocks later. So I put in a comment
to show that, however unreasonable it might seem, it was intentional. I will
admit that sometimes the comments can be cryptic, because they reflect my
state of mind at the time of writing. An example would be your snippet,
modified to include comment

goto 100
* Can we flush the instruction pre-fetch queue? Or will the optimizer win?
100 continue

Such are not intentionally obscure, just accidentally. But at least they indicate
intention and not accident in the coding.
Richard Maine
2007-03-28 16:54:30 UTC
Permalink
Post by Kevin G. Rhoads
Post by Richard Maine
There can be valid reasons for all these constructs. I've even
written at least the last two. But they confuse readers into wondering
whether they are actually supposed to do something or not.
Isn't that one of the things comments are for?
Well. In some cases. But look at the context of the thread that lead to
this. The two suggestions as to why the coding practice in question was
nice were:

1. It was "obvious" what it was doing

and

2. It provided something handy to grep for.

I'd say that

1. If the code needs a comment to explain it, then it must not be all
that obvious.

2. If the purpose is to put something handily grepable, and you need a
comment to explain that, then one might as well put the grepable
material in the comment in the first place and dispense with the
do-nothing code.
--
Richard Maine | Good judgement comes from experience;
email: last name at domain . net | experience comes from bad judgement.
domain: summertriangle | -- Mark Twain
e p chandler
2007-03-21 14:20:40 UTC
Permalink
Post by Brooks Moses
Post by watsteel
I have a pair of If-then blocks to store a minimum residual.
While debugging, I noticed that a 'Print' statement placed within the
second block would change the program's output. I've tried 'Print *',
Write(both to * and to unit=2, which was opened.), and I've changed
the location of the 'Print' statement within the block, but all to no
avail. If the 'Print' statement is in the block, the output changes.
There are two sets of output, depending on whether the 'Print'
statement exists in the block or not.
The program is only 195 lines of code. The input file if 15 lines of
code.
I will happily e-mail them to anyone who can spare the time to look
this over--I'm stumped.
I'm using GNU G77, on a Dell laptop, running XP Home.
And my forehead is getting bruised from banging it on my desk, so any
help would be greatly appreciated.
Presuming that Colin's conclusion that this isn't the usual sort of
out-of-bounds error causing that problem, and that it is in fact a
compiler bug, I think I've got a fair guess which exactly compiler bug
it is, because I've run into it in exactly this situation:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323.
The short version is of the bug this: The Intel floating-point system
has 80-bit floating-point registers, but has only 64-bit floating-point
storage. Thus, there are cases where, if a value is stored in memory,
its value will be slightly different (due to removing the last 16 bits)
than if its kept in a register.
How this hit me is that I had two numbers where A was supposed to be
greater than or equal to B, but when the comparison was done, one of
them had been stored in memory and the other was still in the register
where it had been calculated, and as a result A.GE.B was false.
Printing out something in the middle of this caused all of the variables
to be shunted out of registers into 64-bit memory slots, and thus made
the bug go away.
One recommended fix for this in GCC/g77/gfortran is the -ffloat-store
option, which forces all floating-point values to go through a memory
store before being used, thereby assuring that they're consistently
truncated to 64 bits. It's worth a try to see if using that flag causes
your problem to disappear. (It does slow down the program a bit, though.)
- Brooks
IMO the real solution is for the OP to fix his program and for him to
take a good look at his input data.

Here is the OPs input data with the changes in the predicted values
and the estimated "parameters" induced by using both an "-O" switch
and inserting a Print in the inner loop:

t(I) Fy(I) R(I) N(I) h(I) Pt(I) Pn

.7910 335.58 3.368 25.0 66.193 2.11 3.13 ->
3.27
.7910 335.58 3.368 25.0 66.193 2.16 3.13 ->
3.27
.7910 335.58 3.368 25.0 66.193 2.20 3.13 ->
3.27

.7910 335.58 3.368 25.0 83.682 1.86 3.58 ->
3.73
.7910 335.58 3.368 25.0 83.682 2.12 3.58 ->
3.73

1.078 307.72 3.175 25.0 83.494 3.57 3.44 ->
3.55
1.078 307.72 3.175 25.0 83.494 4.04 3.44 ->
3.55

1.078 307.72 3.175 25.0 90.007 3.96 3.59 ->
3.70
1.078 307.72 3.175 25.0 90.007 4.25 3.59 ->
3.70
1.078 307.72 3.175 25.0 90.007 4.38 3.59 ->
3.70
1.078 307.72 3.175 25.0 90.007 4.46 3.59 ->
3.70

1.547 391.01 3.186 25.0 127.68 7.10 5.66 ->
5.71
1.547 391.01 3.186 25.0 127.68 7.32 5.66 ->
5.71

1.874 453.80 3.195 25.0 153.41 5.75 6.75 ->
6.62
1.874 453.80 3.195 25.0 153.41 6.46 6.75 ->
6.62

iCf = 2.
iCRf = 1.0001 -> 0.9751
iCNf = 0.0001
iChf = 0.8751 -> 0.9501

I've grouped and moved the input data to show that there are multiple
Pt() values for the same set of other input values. This seems odd in
a regression program. Also note that the fitted data is not that close
to the observed data. Note that the step size in the inner loops is
relatively large = .025. Adding a Print statement to the inner loop
changes CR by 1 step and CH by 3 steps when compiled with "-O".

I don't understand why the OP used a "-O" switch anyway. He could have
reduced the run time considerably without resorting to "optimization"
by noting that he is re-computing quantities that are constant for a
particular value of I each time through the inner-most loop.

Instead of

Pn=iC*(t(I)**2)*(Fy(I)/1.d3)*(1-iCR*SQRT(R(I)/t(I)))*
& (1+iCN*SQRT(N(I)/t(I)))*(1-iCh*SQRT(h(I)/t(I)))


Why not pre-compute

x1(i)=(t(I)**2)*(Fy(I)/1.d3)
x2(i)=SQRT(R(I)/t(I)))
x3(i)=SQRT(N(I)/t(I)))
x4(i)=SQRT(h(I)/t(I)))

?

I would also read in M, the number of data points before reading the
data, but that's my preference.

Changing some variable names and using INTEGER DO loops, the core of
the program becomes

msres=1e39 ! a large number
do i1=0,40
C = 0 + i1*.5 ! 0 to 20 step .5
do i2=0,40
CR = .0001 + i2*.025 ! .0001 to 1.0001 step .025
do i3=0,40
CN = .0001 + i3*.025
do i4=0,40
CH = .0001 + i4*.025
sres=0
do i=1,M
Pn=C*x1(i)*(1-CR*x2(i))*(1+CN*x3(i))*(1-CH*x4(i))
sres=(Pn-Pt(i))**2
end do
if (sres < msres) then
msres = sres
CF = C
CRF = CR
CNF = CN
CHF = CH
end if
end do
end do
end do
end do
[blah, blah, blah]

[flame on]

Whether the OP uses G77 or G95 or Gfortran or whether he uses the
latest F95+ syntax or whether hs uses DOUBLE PRECISION instead of REAL
is of little relevance given the numerical instability of his program
and data (IMO).

[flame off]

-- elliot
watsteel
2007-03-21 15:24:34 UTC
Permalink
Post by e p chandler
Post by Brooks Moses
Post by watsteel
I have a pair of If-then blocks to store a minimum residual.
Post by watsteel
While debugging, I noticed that a 'Print' statement placed within the
second block would change the program's output. I've tried 'Print *',
Write(both to * and to unit=2, which was opened.), and I've changed
the location of the 'Print' statement within the block, but all to no
avail. If the 'Print' statement is in the block, the output changes.
There are two sets of output, depending on whether the 'Print'
statement exists in the block or not.
The program is only 195 lines of code. The input file if 15 lines of
code.
I will happily e-mail them to anyone who can spare the time to look
this over--I'm stumped.
I'm using GNU G77, on a Dell laptop, running XP Home.
And my forehead is getting bruised from banging it on my desk, so any
help would be greatly appreciated.
Presuming that Colin's conclusion that this isn't the usual sort of
out-of-bounds error causing that problem, and that it is in fact a
compiler bug, I think I've got a fair guess which exactly compiler bug
it is, because I've run into it in exactly this situation:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323.
The short version is of the bug this: The Intel floating-point system
has 80-bit floating-point registers, but has only 64-bit floating-point
storage. Thus, there are cases where, if a value is stored in memory,
its value will be slightly different (due to removing the last 16 bits)
than if its kept in a register.
How this hit me is that I had two numbers where A was supposed to be
greater than or equal to B, but when the comparison was done, one of
them had been stored in memory and the other was still in the register
where it had been calculated, and as a result A.GE.B was false.
Printing out something in the middle of this caused all of the variables
to be shunted out of registers into 64-bit memory slots, and thus made
the bug go away.
One recommended fix for this in GCC/g77/gfortran is the -ffloat-store
option, which forces all floating-point values to go through a memory
store before being used, thereby assuring that they're consistently
truncated to 64 bits. It's worth a try to see if using that flag causes
your problem to disappear. (It does slow down the program a bit, though.)
- Brooks
IMO the real solution is for the OP to fix his program and for him to
take a good look at his input data.
Here is the OPs input data with the changes in the predicted values
and the estimated "parameters" induced by using both an "-O" switch
t(I) Fy(I) R(I) N(I) h(I) Pt(I) Pn
.7910 335.58 3.368 25.0 66.193 2.11 3.13 ->
3.27
.7910 335.58 3.368 25.0 66.193 2.16 3.13 ->
3.27
.7910 335.58 3.368 25.0 66.193 2.20 3.13 ->
3.27
.7910 335.58 3.368 25.0 83.682 1.86 3.58 ->
3.73
.7910 335.58 3.368 25.0 83.682 2.12 3.58 ->
3.73
1.078 307.72 3.175 25.0 83.494 3.57 3.44 ->
3.55
1.078 307.72 3.175 25.0 83.494 4.04 3.44 ->
3.55
1.078 307.72 3.175 25.0 90.007 3.96 3.59 ->
3.70
1.078 307.72 3.175 25.0 90.007 4.25 3.59 ->
3.70
1.078 307.72 3.175 25.0 90.007 4.38 3.59 ->
3.70
1.078 307.72 3.175 25.0 90.007 4.46 3.59 ->
3.70
1.547 391.01 3.186 25.0 127.68 7.10 5.66 ->
5.71
1.547 391.01 3.186 25.0 127.68 7.32 5.66 ->
5.71
1.874 453.80 3.195 25.0 153.41 5.75 6.75 ->
6.62
1.874 453.80 3.195 25.0 153.41 6.46 6.75 ->
6.62
iCf = 2.
iCRf = 1.0001 -> 0.9751
iCNf = 0.0001
iChf = 0.8751 -> 0.9501
I've grouped and moved the input data to show that there are multiple
Pt() values for the same set of other input values. This seems odd in
a regression program. Also note that the fitted data is not that close
to the observed data. Note that the step size in the inner loops is
relatively large = .025. Adding a Print statement to the inner loop
changes CR by 1 step and CH by 3 steps when compiled with "-O".
I don't understand why the OP used a "-O" switch anyway. He could have
reduced the run time considerably without resorting to "optimization"
by noting that he is re-computing quantities that are constant for a
particular value of I each time through the inner-most loop.
Instead of
Pn=iC*(t(I)**2)*(Fy(I)/1.d3)*(1-iCR*SQRT(R(I)/t(I)))*
& (1+iCN*SQRT(N(I)/t(I)))*(1-iCh*SQRT(h(I)/t(I)))
Why not pre-compute
x1(i)=(t(I)**2)*(Fy(I)/1.d3)
x2(i)=SQRT(R(I)/t(I)))
x3(i)=SQRT(N(I)/t(I)))
x4(i)=SQRT(h(I)/t(I)))
?
I would also read in M, the number of data points before reading the
data, but that's my preference.
Changing some variable names and using INTEGER DO loops, the core of
the program becomes
msres=1e39 ! a large number
do i1=0,40
C = 0 + i1*.5 ! 0 to 20 step .5
do i2=0,40
CR = .0001 + i2*.025 ! .0001 to 1.0001 step .025
do i3=0,40
CN = .0001 + i3*.025
do i4=0,40
CH = .0001 + i4*.025
sres=0
do i=1,M
Pn=C*x1(i)*(1-CR*x2(i))*(1+CN*x3(i))*(1-CH*x4(i))
sres=(Pn-Pt(i))**2
end do
if (sres < msres) then
msres = sres
CF = C
CRF = CR
CNF = CN
CHF = CH
end if
end do
end do
end do
end do
[blah, blah, blah]
[flame on]
Whether the OP uses G77 or G95 or Gfortran or whether he uses the
latest F95+ syntax or whether hs uses DOUBLE PRECISION instead of REAL
is of little relevance given the numerical instability of his program
and data (IMO).
[flame off]
-- elliot
What does 'OP' mean?
Also, each line in the data file presents the results of a particular
test, and some of the test parameters. This may make the reason for
the "multiple Pt() values for the same set of other input values" a
little more clear.
Post by e p chandler
sres=0
do i=1,M
Pn=C*x1(i)*(1-CR*x2(i))*(1+CN*x3(i))*(1-CH*x4(i))
sres=(Pn-Pt(i))**2
end do
if (sres < msres) then
msres = sres
watsteel
2007-03-21 15:29:39 UTC
Permalink
Post by watsteel
Post by e p chandler
Post by Brooks Moses
Post by watsteel
I have a pair of If-then blocks to store a minimum residual.
Post by watsteel
While debugging, I noticed that a 'Print' statement placed within the
second block would change the program's output. I've tried 'Print *',
Write(both to * and to unit=2, which was opened.), and I've changed
the location of the 'Print' statement within the block, but all to no
avail. If the 'Print' statement is in the block, the output changes.
There are two sets of output, depending on whether the 'Print'
statement exists in the block or not.
The program is only 195 lines of code. The input file if 15 lines of
code.
I will happily e-mail them to anyone who can spare the time to look
this over--I'm stumped.
I'm using GNU G77, on a Dell laptop, running XP Home.
And my forehead is getting bruised from banging it on my desk, so any
help would be greatly appreciated.
Presuming that Colin's conclusion that this isn't the usual sort of
out-of-bounds error causing that problem, and that it is in fact a
compiler bug, I think I've got a fair guess which exactly compiler bug
it is, because I've run into it in exactly this situation:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323.
The short version is of the bug this: The Intel floating-point system
has 80-bit floating-point registers, but has only 64-bit floating-point
storage. Thus, there are cases where, if a value is stored in memory,
its value will be slightly different (due to removing the last 16 bits)
than if its kept in a register.
How this hit me is that I had two numbers where A was supposed to be
greater than or equal to B, but when the comparison was done, one of
them had been stored in memory and the other was still in the register
where it had been calculated, and as a result A.GE.B was false.
Printing out something in the middle of this caused all of the variables
to be shunted out of registers into 64-bit memory slots, and thus made
the bug go away.
One recommended fix for this in GCC/g77/gfortran is the -ffloat-store
option, which forces all floating-point values to go through a memory
store before being used, thereby assuring that they're consistently
truncated to 64 bits. It's worth a try to see if using that flag causes
your problem to disappear. (It does slow down the program a bit, though.)
- Brooks
IMO the real solution is for the OP to fix his program and for him to
take a good look at his input data.
Here is the OPs input data with the changes in the predicted values
and the estimated "parameters" induced by using both an "-O" switch
t(I) Fy(I) R(I) N(I) h(I) Pt(I) Pn
.7910 335.58 3.368 25.0 66.193 2.11 3.13 ->
3.27
.7910 335.58 3.368 25.0 66.193 2.16 3.13 ->
3.27
.7910 335.58 3.368 25.0 66.193 2.20 3.13 ->
3.27
.7910 335.58 3.368 25.0 83.682 1.86 3.58 ->
3.73
.7910 335.58 3.368 25.0 83.682 2.12 3.58 ->
3.73
1.078 307.72 3.175 25.0 83.494 3.57 3.44 ->
3.55
1.078 307.72 3.175 25.0 83.494 4.04 3.44 ->
3.55
1.078 307.72 3.175 25.0 90.007 3.96 3.59 ->
3.70
1.078 307.72 3.175 25.0 90.007 4.25 3.59 ->
3.70
1.078 307.72 3.175 25.0 90.007 4.38 3.59 ->
3.70
1.078 307.72 3.175 25.0 90.007 4.46 3.59 ->
3.70
1.547 391.01 3.186 25.0 127.68 7.10 5.66 ->
5.71
1.547 391.01 3.186 25.0 127.68 7.32 5.66 ->
5.71
1.874 453.80 3.195 25.0 153.41 5.75 6.75 ->
6.62
1.874 453.80 3.195 25.0 153.41 6.46 6.75 ->
6.62
iCf = 2.
iCRf = 1.0001 -> 0.9751
iCNf = 0.0001
iChf = 0.8751 -> 0.9501
I've grouped and moved the input data to show that there are multiple
Pt() values for the same set of other input values. This seems odd in
a regression program. Also note that the fitted data is not that close
to the observed data. Note that the step size in the inner loops is
relatively large = .025. Adding a Print statement to the inner loop
changes CR by 1 step and CH by 3 steps when compiled with "-O".
I don't understand why the OP used a "-O" switch anyway. He could have
reduced the run time considerably without resorting to "optimization"
by noting that he is re-computing quantities that are constant for a
particular value of I each time through the inner-most loop.
Instead of
Pn=iC*(t(I)**2)*(Fy(I)/1.d3)*(1-iCR*SQRT(R(I)/t(I)))*
& (1+iCN*SQRT(N(I)/t(I)))*(1-iCh*SQRT(h(I)/t(I)))
Why not pre-compute
x1(i)=(t(I)**2)*(Fy(I)/1.d3)
x2(i)=SQRT(R(I)/t(I)))
x3(i)=SQRT(N(I)/t(I)))
x4(i)=SQRT(h(I)/t(I)))
?
I would also read in M, the number of data points before reading the
data, but that's my preference.
Changing some variable names and using INTEGER DO loops, the core of
the program becomes
msres=1e39 ! a large number
do i1=0,40
C = 0 + i1*.5 ! 0 to 20 step .5
do i2=0,40
CR = .0001 + i2*.025 ! .0001 to 1.0001 step .025
do i3=0,40
CN = .0001 + i3*.025
do i4=0,40
CH = .0001 + i4*.025
sres=0
do i=1,M
Pn=C*x1(i)*(1-CR*x2(i))*(1+CN*x3(i))*(1-CH*x4(i))
sres=(Pn-Pt(i))**2
end do
if (sres < msres) then
msres = sres
CF = C
CRF = CR
CNF = CN
CHF = CH
end if
end do
end do
end do
end do
[blah, blah, blah]
[flame on]
Whether the OP uses G77 or G95 or Gfortran or whether he uses the
latest F95+ syntax or whether hs uses DOUBLE PRECISION instead of REAL
is of little relevance given the numerical instability of his program
and data (IMO).
[flame off]
-- elliot
What does 'OP' mean?
Also, each line in the data file presents the results of a particular
test, and some of the test parameters. This may make the reason for
the "multiple Pt() values for the same set of other input values" a
little more clear.
Post by e p chandler
sres=0
do i=1,M
Pn=C*x1(i)*(1-CR*x2(i))*(1+CN*x3(i))*(1-CH*x4(i))
sres=(Pn-Pt(i))**2
end do
if (sres < msres) then
msres = sres
Oops, hit <Enter> to soon!

I like the msres=1e39 ! a large number plus the rest of that. A lot
cleaner.
Like I said, I'm a newbie. In my blind ignorance, the -O seemed like a
good idea at the time.
Post by watsteel
Post by e p chandler
I would also read in M, the number of data points before reading the
data, but that's my preference.
I had originally done this, but it seemed to me that 'DO WHILE
(.TRUE.) ' would suffice. My bad.

Thanks for the comments. I'll look the rest over.

Cheers,

Vic
Paul van Delst
2007-03-21 15:32:15 UTC
Permalink
Post by watsteel
What does 'OP' mean?
Original poster. I.e. the person that initiated the thread (in this case, you).

cheers,

paulv
--
Paul van Delst Ride lots.
CIMSS @ NOAA/NCEP/EMC Eddy Merckx
e p chandler
2007-03-22 16:33:17 UTC
Permalink
Post by e p chandler
Post by watsteel
I have a pair of If-then blocks to store a minimum residual.
Changing some variable names and using INTEGER DO loops, the core of
the program becomes
msres=1e39 ! a large number
do i1=0,40
C = 0 + i1*.5 ! 0 to 20 step .5
do i2=0,40
CR = .0001 + i2*.025 ! .0001 to 1.0001 step .025
do i3=0,40
CN = .0001 + i3*.025
do i4=0,40
CH = .0001 + i4*.025
sres=0
do i=1,M
Pn=C*x1(i)*(1-CR*x2(i))*(1+CN*x3(i))*(1-CH*x4(i))
sres=(Pn-Pt(i))**2
OOPS it should of course be

sres=sres+(Pn-Pt(i))**2
Post by e p chandler
end do
if (sres < msres) then
msres = sres
CF = C
CRF = CR
CNF = CN
CHF = CH
end if
end do
end do
end do
end do
Excuse the previous mini flame fest :-). Along with all of the other
worthwhile suggestions, I do think it is worth the OP's time to
investigate the numerical stability of his program. Storing the "x"
arrays allows decreasing the step size and increasing the number of
steps while keeping a reasonable run time.

As for the different Pt() values for the same test conditions, I would
combine these data points by taking the average of the corresponding
Pt() values. This reduces the number of input data points and also
decreases the run time without changing the output much.

As a check on the "goodness of fit" of the regression, I would suggest
computing a standard error (of the mean) for each set of measurements,
then computing for each "fit" (actual - fitted)/standard_error.
Since there are now only 6 data points, this is easy enough to do by
hand.

-- elliot
Loading...