Compiler bug ?

Compiler bug ?

This simple program is supposed to calculate principal and interest as you make payments.

If the payment is <= the remaining principal,  the payment amount is reduced accordingly.

When I run it , it does NOT show that I set the principal (ypr) = to 0.

In other words, line 19 gets skipped in the compiled code, where I set ypr= 0.

Am I missing something here? Why would the compiler ignore that line?

29 post / 0 nuovi
Ultimo contenuto
Per informazioni complete sulle ottimizzazioni del compilatore, consultare l'Avviso sull'ottimizzazione

Why does it post this multiple times?

It looks like it hung up, and it asks me for MEDIA?

What does it want?

Don't they test this stuff?

Allegati: 

AllegatoDimensione
Download test21.f90799 byte

It's just waiting for input (by default, from keyboard).   

Surprisingly, all this IBM-360ish non-Fortran stuff seems to work with several current compilers.

It doesn't look like you ever satisfied the condition ypr <= pay

Ritratto di mecej4

The DO loop runs the specified number of times without the IF..ENDIF block being executed. The IF condition is not satisfied until after the 360th iteration of the loop.

I guess you did not actually run the program.

 

You can put in a payment amount that will make it quit early.

You still see the same problem.

I put in a PRINT statement BEFORE line 19, and then it 

DOES execute that AND the next line.

So putting in the PRINT statement somehow fixes the compiler problem.

I see what you mean about the 360th time it executes the Do LOOP.

But I don't see why putting in a PRINT statement would change anything.

 

Ritratto di mecej4

There is no problem that I can see. Add a statement such as

write(*,*)' IF TEST SUCCEEDED'

after the IF statement, put in the payment amount that you want, and rerun the program.

If you still think that there is a problem, you would need to provide details such as compiler version, options used, etc.

That READ(*,*) statement is just to keep the program from quitting before I can read the window contents.

Surprisingly, in a GRAPHICS program, we don't need to do that.

It still allows you to examine the window contents before it actually quits,

but that is not true for CONSOLE programs.

Ritratto di app4619

The condition would be satisfied the 361st time around the loop but you stop at 360.

For console you can use the PAUSE command to wait at the end. I really would not recomment  " implicit real*8 (A-H,O-Z)", use implicit none and declare everthing. It saves time in the long run.

I write some software and customers contact me sometimes saying there is a bug. More than 90% of the time they are doing something wrong or the software is doing what it should but they think it should be doing something different. It irritates me a bit on some occaisons. When I have a problem I start with "I have a problem with... " or  "I don't undertstand why...." . Bug may be the terms that is appropraite having been through the issue in detail but isn't the starting point IMO.

That's why I put a question mark after "bug."

Cause I wasn't sure - - - 

One of the other guys suggested the READ(*,*) approach.

They said PAUSE was an obsolete feature, so I tried that.

Bill,

The repayment for 360 monthly payments at nominal 4.5%; principal = 195,000, n = 360, r = .045/12 = 0.375% )

monthly repayment = principal * r * (1+r)^n / ( (1+r)^n - 1) = 988.04

where the repayment ratio = (1+r)^n / ( (1+r)^n - 1)

This formula ignores the rounding policy for interest calculation and also assumes monthly interest = annual interest / 12.

If you estimate a monthly compounding interest rate equivalent to an annual rate of 4.5%, this is 0.3675%, with a resulting monthly repayment of 977.6. ( my apologies if I have these numbers wrong, but it demonstrates the problem.) You calculated rate is really paying nearly 4.6%.

You need to read the fine print on how monthly interest is defined.

Your estimate of 987 extended the pain by 1 month !!

John

ps: I increased the repayment to 998 and it stopped at payment 353, with the if test being activated.

Actually, this first came up when I put in a payment of $988.04

That's what the bank is charging me for my mortgage.

 

But this is a tiny amount too small.

If I change it to $988.05, the last payment (#360) will in fact pay it off.

I was experimenting with different payment amounts to see what would happen.

THanks to all who responded.

Ritratto di Steve Lionel (Intel)

Some comments of my own:

The statement:

xi=xint/100.0/12.0

is using both single-precision and double-precision in the calculation. In this case it may be harmless, but in general using mixed-mode arithmetic can lead to loss of precision.

Also, when computing with currency, use of binary floating point can be problematic due to rounding issues. I've attached an amortization program I wrote many years back that uses scale factors to convert the input values to integer-valued floats so that there's no loss of precision.

Edit: Hmm, attach not working. I'll insert it inline for now and see what's going on:

PROGRAM AMORT
!+

	! This program performs loan amortization calculations for loans which

	! are paid monthly.  It uses scaled double-precision values in order to

	! avoid losing fractions of cents.  The P format is used to scale input

	! values to cents on input and back to dollars on output.

	!

	! Author: Steve Lionel

	!

	!-
IMPLICIT NONE
INTEGER, PARAMETER :: DP = SELECTED_REAL_KIND(15)

	REAL(DP) :: RATE,RATE_PER_PAYMENT

	INTEGER  :: NUMBER_OF_PAYMENTS

	REAL(DP) :: REMAINING_PRINCIPAL

	REAL(DP) :: MONTHLY_PAYMENT,SCHEDULED_PAYMENT

	REAL(DP) :: MONTHLY_PRINCIPAL

	REAL(DP) :: MONTHLY_INTEREST

	REAL(DP) :: PRINCIPAL_FACTOR

	REAL(DP) :: YTD_PRINCIPAL,YTD_INTEREST

	REAL(DP) :: TOTAL_INTEREST

	INTEGER  :: YEAR_NUMBER, MONTH_NUMBER, DAY_NUMBER, PAYMENT_NUMBER, DAYTEMP

	LOGICAL  :: DONE

	TYPE T_M

	  INTEGER :: DAYS_IN_MONTH

	  CHARACTER(3) :: MONTHNAME

	  END TYPE T_M

	TYPE(T_M), DIMENSION(12), PARAMETER :: MONTHS = (/ &

	  T_M(31,'Jan'),T_M(28,'Feb'),T_M(31,'Mar'),T_M(30,'Apr'),T_M(31,'May'), T_M(30,'Jun'), &

	  T_M(31,'Jul'),T_M(31,'Aug'),T_M(30,'Sep'),T_M(31,'Oct'),T_M(30,'Nov'), T_M(31,'Dec') /)

	                       

	CHARACTER(200) FILESPEC

	CHARACTER YN
11  FORMAT (A)

	12  FORMAT (BN,2PG10.0)

	13  FORMAT (BN,I10)

	14  FORMAT (BN,-2PG12.0)

	15  FORMAT (A)
Mainloop: DO

	    WRITE (*,11,ADVANCE='NO') ' Enter interest rate in percent, ^Z to exit: '

	    READ (*,12,END=999) RATE

	    RATE_PER_PAYMENT = RATE/12.0D0

	    WRITE (*,11,ADVANCE='NO') ' Enter number of months: '

	    READ (*,13,END=999) NUMBER_OF_PAYMENTS

	    WRITE (*,11,ADVANCE='NO') ' Enter principal amount: '

	    READ (*,14,END=999) REMAINING_PRINCIPAL
    PRINCIPAL_FACTOR = 1.0D0 - ((RATE_PER_PAYMENT+1.0D0)** (-NUMBER_OF_PAYMENTS))

	    PRINCIPAL_FACTOR = RATE_PER_PAYMENT / PRINCIPAL_FACTOR
    SCHEDULED_PAYMENT = DNINT(REMAINING_PRINCIPAL * PRINCIPAL_FACTOR)

	    TOTAL_INTEREST = (NUMBER_OF_PAYMENTS * SCHEDULED_PAYMENT) - &

	    REMAINING_PRINCIPAL
101 FORMAT ( ' Principal amount = $',-2PF9.2)

	102 FORMAT ( ' Interest rate    =  ',2PF9.3,'%')

	103 FORMAT ( ' Duration of loan =  ',I9,' months')

	104 FORMAT (/' Monthly payment  = $',-2PF9.2)

	105 FORMAT ( ' Total interest   = $',-2PF9.2)
    WRITE (*,101) REMAINING_PRINCIPAL

	    WRITE (*,102) RATE

	    WRITE (*,103) NUMBER_OF_PAYMENTS

	    WRITE (*,104) SCHEDULED_PAYMENT

	    WRITE (*,105) TOTAL_INTEREST
    DO

	        WRITE (*,11,ADVANCE='NO') ' Do you wish an amortization report? [Y]'

	        READ (*,15,END=999) YN

	        IF (YN == 'Y' .OR. YN == 'y' .OR. YN == ' ') EXIT

	        IF (YN == 'N' .OR. YN == 'n') EXIT Mainloop

	        WRITE (*,11) ' Please respond with Y or N'

	        END DO
    WRITE (*,11,ADVANCE='NO') ' Enter loan year number (2005, etc.): '

	    READ (*,13,END=999) YEAR_NUMBER

	    WRITE (*,11,ADVANCE='NO') ' Enter loan month number (1-12): '

	    READ (*,13,END=999) MONTH_NUMBER

	    WRITE (*,11,ADVANCE='NO') ' Enter loan day number (1-31): '

	    READ (*,13,END=999) DAY_NUMBER

	    WRITE (*,11,ADVANCE='NO') ' Enter file specification for report: '

	    READ (*,15) FILESPEC

	    OPEN (UNIT=1,FILE=FILESPEC,STATUS='REPLACE',FORM='FORMATTED')

	    WRITE (1,101) REMAINING_PRINCIPAL

	    WRITE (1,102) RATE

	    WRITE (1,103) NUMBER_OF_PAYMENTS

	    WRITE (1,104) SCHEDULED_PAYMENT

	    WRITE (1,105) TOTAL_INTEREST

	106 FORMAT (///,T30,'Calendar year ',I4// &

	        '     Payment',T25,'Remaining',T37,'Principal',T48, &

	        'Interest',T61,'Total'/ &

	        '   #     Date',  T25,'Principal',T39,'Payment', T49, &

	        'Payment', T59,'Payment'/ &

	        1X,3('-------------------------'))

	107    FORMAT (1X,I3,1X,I2,'-',A3,'-',I4,2X,-2PF15.2,2X,3(-2PF10.2))

	109    FORMAT (//' Year-to-date interest paid    = $',-2PF9.2/ &

	              ' Year-to-date principal paid   = $',-2PF9.2/ &

	              ' End-of-year principal balance = $',-2PF9.2)
    DONE = .FALSE.

	    DO WHILE (PAYMENT_NUMBER < NUMBER_OF_PAYMENTS)

	        IF (MONTH_NUMBER < 12) WRITE (1,106) YEAR_NUMBER
        YTD_INTEREST = 0

	        YTD_PRINCIPAL = 0

	        

	        ! Do up until the end of this calendar year

	        DO WHILE ((MONTH_NUMBER < 12) .AND. .NOT. DONE)

	            PAYMENT_NUMBER = PAYMENT_NUMBER + 1

	            MONTH_NUMBER = MONTH_NUMBER + 1

	            MONTHLY_INTEREST = DNINT(REMAINING_PRINCIPAL * RATE_PER_PAYMENT)

	            MONTHLY_PRINCIPAL = SCHEDULED_PAYMENT - MONTHLY_INTEREST

	            ! Last payment?

	            IF (PAYMENT_NUMBER == NUMBER_OF_PAYMENTS) THEN

	                MONTHLY_PRINCIPAL = REMAINING_PRINCIPAL

	                DONE = .TRUE.

	                END IF

	            MONTHLY_PAYMENT = MONTHLY_INTEREST + MONTHLY_PRINCIPAL

	            DAYTEMP = MIN(DAY_NUMBER, MONTHS(MONTH_NUMBER)%DAYS_IN_MONTH)

	            IF ((MONTH_NUMBER == 2) .AND. (MOD(YEAR_NUMBER,4) == 0)) &

	              DAYTEMP = MIN(DAY_NUMBER, 29)

	            WRITE (1,107) PAYMENT_NUMBER, DAYTEMP, &

	              MONTHS(MONTH_NUMBER)%MONTHNAME,YEAR_NUMBER, &

	              REMAINING_PRINCIPAL, MONTHLY_PRINCIPAL, &

	              MONTHLY_INTEREST, MONTHLY_PAYMENT

	            REMAINING_PRINCIPAL = REMAINING_PRINCIPAL - MONTHLY_PRINCIPAL

	            YTD_INTEREST = YTD_INTEREST + MONTHLY_INTEREST

	            YTD_PRINCIPAL = YTD_PRINCIPAL + MONTHLY_PRINCIPAL

	            END DO
        ! Print year-to-date summary

	        IF (PAYMENT_NUMBER > 0) WRITE (1,109) YTD_INTEREST, &

	            YTD_PRINCIPAL, REMAINING_PRINCIPAL

	        YEAR_NUMBER = YEAR_NUMBER + 1

	        MONTH_NUMBER = 0

	        END DO

	    CLOSE (UNIT=1)

	    END DO Mainloop

	999 END

Steve

Hi steve;

I am going to try this code, see what it does.

A questyion did come up  about the use of those constants.

Since 12 is numerically identical to 12.0 or even 12.D0,

would it be necessary to say something like:

Xi =xint /dble(12.0) / dble(100.0)

 or xi=xint/12.d0 /100.d0

In other words, would the compiler automatically know to convert all quantiites to DBL

​precision before doing the divides?

 

I could have said: xi=xint/12/100 couldn't I?

 

Maybe I am assuming too much here.

 

Ritratto di Steve Lionel (Intel)

If you said xint/12/100 the compiler would be free to evaluate this as xint / dble(12/100) which would end up giving you a zerodivide error, since 12/100 is 0 in integer. This is the sort of thing that should make you avoid mixed-mode arithmetic everywhere.

Steve

I disagree that the compiler is free to change xint/12/100 (assuming xint is real or dp)

​to xint/dble(12/100).  xint/12/100 and xint/(12/100) are *not* mathematically

equivalent, because of the difference between real and integer division.

Semantics of the language say interpretation must be left to right with

mulitple /s. After the interpretation is established, then the compiler

can make changes that are mathematically equivalent. So it could

change it to xint/100/12, for example, or even xint/1200.0.

Ritratto di Steve Lionel (Intel)

Ah, yes. Walt is correct here - my apologies. Nevertheless, mixed-mode arithmetic is to be avoided.

Steve

but xint/(12/100) is algebraically = to xint *100 /12

when you divide by a fraction, that is the same as inverting it and multiplying it.78

I was told that the compiler proceeds from left to right when it determines the precision of an operation,

so if two numbers have a different precision, it picks the higher precision.

Now would the compiler first convert 100 to a double precision number, THEN multiply it?

Since the result is now Double precision, wouldn't it then convert 12 to a D. P. number before multiplying it?

I agree that putting the 12/100 inside a parenthesis is asking for trouble, cause you are forcing

the compiler to use integer arithmetic.

xint/12/100 is algebraically = to (xint/12)/100

 

I don't think the compiler is supposed to ignore the rules of 9th grade algebra, is it?

All these give the EXACT same answer, BTW.

real*8 xint/4.5D0/
  print *,xint/12/100
  print *,xint/12./100.
  print *,xint/(12*100)
  print *,xint/(12.d0*100)
  print *,xint/1200.D0
  read(*,*)
  end

 

Ritratto di Steve Lionel (Intel)

When you have xint/100/12, the compiler does xint/100 first. According to the rules of mixed-mode arithmetic in the standard, the 100 is converted to the type and kind of xint (real(8) here.  Then the divide by 12 is done, and again the 12 is converted to real(8) for the division.

Steve

I agree there, and I also see why xint/(12/100) gives you.a divide by zero.

You are forcing integer arithmetic there, too.

but since 12*100 =1200, and 12.D0 *100.D0 =1200.d0 ,then

multiply by (12*100) gives the same answer as multiply by 1200.D0

Sloppy programming, perhaps - - - -

 

You might get into trouble with integer overflow, since

the compiler does NOT check for that.

For example 257*257 would possibly give a wrong answer.

How would the compiler know that the result wont fit into a 2 byte integer?

Does it automatically assume the result will be integer(8)?

Ritratto di Steve Lionel (Intel)

Ah, integer overflow...

First thing to understand is that constants have a type and kind specified by the standard. If you write 257, that is "default integer kind" - in our implementation, INTEGER(4), unless you change it with /integer_size. So 257*257 is still INTEGER(4) and that's fine. So what happens if you do something like 2147483647 + 6? Both of those are INTEGER(4) and the result overflows an INTEGER(4). It is especially important to understand that Fortran doesn't care about the kind of variable you assign such an expression to. So if you have:

integer(8) P
P = 2147483647 + 6
print *, p

it prints -2147483643 and there's no run-time error. We don't do integer overflow checking at this time.

So, no, there is no "assumption" that an expression which overflows should be considered a larger kind. In Fortran 77, where the standard didn't have more than one kind of integer, it was common for compilers that did support different kinds to give you free extension. Same holds for things such as:

DOUBLE PRECISION PI
PI=3.1415926535897

in some F77 compilers, the constant would be evaluated in maximum precision and then assigned to the variable. As of Fortran 90, that was no longer allowed (though we have an option to do it.) Otherwise it's a "default real" constant and you lose the extra digits.

Steve
Ritratto di David White

Steve,

Your explanation of how xint/100/12 is evaluated does not hold up.  I do not believe the standard specifies left to right evaluation, so the compiler is not obliged to evaluate in the order you specify.  It is possible that 100/12 is evaluated first (to 8), and then xint/8 is evaluated.

David

Ritratto di Steve Lionel (Intel)

No, Walt was essentially correct. The standard says: "Once the interpretation of a numeric intrinsic operation is established, the processor may evaluate any mathematically equivalent expression, provided that the integrity of parentheses is not violated." Since division is not associative, the transformation I suggested would not be allowed. This has nothing to do with left-to-right.

 

Steve

Steve,

Your example is very useful to identify a problem that can vary the results between different F90+ compilers.
If the programmer has provided a constant of 14 digits, should the compiler honor this request ?
DOUBLE PRECISION PI
PI=3.1415926535897

The situation gets more confusing (annoying?) if the code is:
DOUBLE PRECISION PI
PI=0.1
 (this is a more common example that has caused problems.)

Also, the constant can be interpreted differently, as in a read statement:
DOUBLE PRECISION PI
  Read ( '3.1415926535897 ',*) PI

as reading data of higher precision will always be read as a constant at the variable's precision.

There are still some compilers that provide backward compatibility and provide maximum precision for constants.
Not providing this precision can be a very annoying result when converting old code, causing lots of program testing to find the run-time difference that results. I'm not sure that the standard got it right in this case.

John

DOUBLE PRECISION PI_1, PI_2, p1, p2
character string*30
 
   string = '3.1415926535897 '
   PI_1 = 3.1415926535897
   Read ( string,*) PI_2
 
   string = '0.1 '
   p1 = 0.1
   Read ( string,*) P2
!
   write (*,*) Pi_1, pi_2, PI_1-Pi_2
   write (*,*) p1, p2, p1-p2
!
end

	

 

 

Actually, PI as a real*8 number should be given to at least 16 digits after the decimal point.

I always generated it by saying

 

PI = atan (1.D0)*4.0

 

That way I get the very last bit.

BUt you can also say :

data PI/3.14159265358979323846D0/

I dont know offhand how many places the compiler allows you to give, but too many is just overkill of course.

If you are working with REAL(16) you would give 30 digits after the decimal point,

just to be consistent.

Ritratto di Steve Lionel (Intel)

Citazione:

John Campbell ha scritto:

Steve,

Your example is very useful to identify a problem that can vary the results between different F90+ compilers.
If the programmer has provided a constant of 14 digits, should the compiler honor this request ?
DOUBLE PRECISION PI
PI=3.1415926535897

This isn't a "request" - the compiler doesn't really know what you intended here, though I suppose it could guess that if you specified significantly more digits than are represented in the precision that perhaps you made a mistake. I know some compilers give you warnings about this (we don't). But it would be non-conforming for the compiler to treat the constant as anything but default real.

Steve

It seems that the Fortran committee must have batted this back and forth, as several compilers existed on either side of the fence when the standard was confirmed.  The compiler I learned on switched floating point constants to implied double when 9 or more digits were .  It didn't increase precision of 8-digit constants even when they were assigned to double.   This and other non-standard stuff like treating x*i/j as x*(i/j) caused more confusion than advantage. 

Fortunately few such legacy problems remain, but I could mention a few in ifort (treating integer as logical, not obvious which standard-semantics options improve and which hurt performance...)

Ritratto di Steve Lionel (Intel)

As I said earlier, Fortran 77 compilers were free to give you the extra precision because the standard didn't have the concept of "kinds". The "compiler you learned on" was likely a F77 compiler and this was a common feature - I know VAX Fortran did

Steve

Accedere per lasciare un commento.