- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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?
링크가 복사됨
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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.
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
There is no problem that I can see. Add a statement such as
[fortran]write(*,*)' IF TEST SUCCEEDED'[/fortran]
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.
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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.
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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.
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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.
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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.
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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:
[fortran]
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?
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[/fortran]
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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.
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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.
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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.
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
Ah, yes. Walt is correct here - my apologies. Nevertheless, mixed-mode arithmetic is to be avoided.
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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.
- 신규로 표시
- 북마크
- 구독
- 소거
- RSS 피드 구독
- 강조
- 인쇄
- 부적절한 컨텐트 신고
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)?