所得税逻辑问题

Income Tax Logic Questions

我在弄清楚这背后的逻辑时遇到了一些麻烦。

我需要显示一份报告,计算每月的余额、利息和本金,直到余额为零。

例如,如果输入为月数=12,余额=25000,利率=4.5%,输出应如下所示:

 months    balance     interest   principal
 1        000.00      .75      ,040.71
 2        ,959.29     .10      ,048.36
 .......
 12       ,126.53      .97       ,126.49

我不确定在 DISPLAY col-hdr 之后和 STOP RUN 之前写什么。有任何想法吗?

   IDENTIFICATION DIVISION.
   PROGRAM-ID. practice.
   DATA DIVISION.
   WORKING-STORAGE SECTION.

   01  LOANFMT    PIC $$$$,$$$,$$$.$$.
   01  LOANAMT    PIC S9(9)V9(2)   VALUE 0.
   01  INTRATE    PIC S9V9(2)      VALUE 0.
   01  INTFMT     PIC 9.999.
   01  NUMMONTHS  PIC S9(3)        VALUE 0.
   01  MONFMT     PIC ZZ9.
   01  MONCNT     PIC S999         VALUE 1.
   01  PMT        PIC S9(9)V9(2)   VALUE 0. 
   01  PMTFMT     PIC $$$$,$$$,$$$..
   01  TOTPMT     PIC S9(9)V9(2)   VALUE 0. 
   01  TOTFMT     PIC $$$$,$$$,$$$..          

   01  col-hdr.
        05                     pic x(15) value "Month".
        05                     pic x(15) value "Balance".
        05                     pic x(15) value "Interest".
        05                     pic x(15) value "Principal".      

   01  Detail-Line.
        05                Pic X(2) Value Spaces.
        05  DL-MONTH      Pic X(999) VALUE 1.
        05                Pic X(5) Value Spaces.
        05  DL-BALANCE    Pic $$$$,$$$,$$$..
        05                Pic X(4) Value Spaces.
        05  DL-INTEREST   Pic $$$$,$$$,$$$..
        05                Pic X(4) Value Spaces.
        05  DL-PRINCIPAL  Pic $$$$,$$$,$$$..            


   PROCEDURE DIVISION.
   000-MAIN SECTION.
       DISPLAY "Enter Loan Amount: " WITH NO ADVANCING
       ACCEPT LOANAMT
       IF 0 > LOANAMT
       PERFORM UNTIL LOANAMT > 0
        DISPLAY "Loan Amount must be positive"
        DISPLAY "Enter Loan Amount: " WITH NO ADVANCING
        ACCEPT LOANAMT
       end-PERFORM
       END-IF           


       DISPLAY "Enter Annual Interest Rate: " WITH NO ADVANCING
       ACCEPT INTRATE
       IF 0 > INTRATE 
          PERFORM UNTIL INTRATE > 0
             DISPLAY "Annual Interest Rate must be positive"
             DISPLAY "Enter Annual Interest Rate: "  WITH 
             NO ADVANCING
        ACCEPT INTRATE
       end-PERFORM
       END-IF

       DISPLAY "Enter Number of Months: " WITH NO ADVANCING
       ACCEPT NUMMONTHS
       IF 0 > NUMMONTHS
       PERFORM UNTIL NUMMONTHS > 0
        DISPLAY "Number of Months must be positive"
        DISPLAY "Enter Number of Months: " WITH NO 
        ADVANCING
        ACCEPT NUMMONTHS
       end-PERFORM
       END-IF

       DISPLAY SPACE     

       move LOANAMT TO LOANFMT
       move INTRATE TO INTFMT
       MOVE NUMMONTHS TO MONFMT
       MOVE PMT TO PMTFMT
       MOVE TOTPMT TO TOTFMT

       DISPLAY col-hdr

       100-init.
          DL-BALANCE = LOANAMT
          DL-INTEREST = LOAN * (INTRATE/NUMMONTHS)
          DL-PRINCIPAL = LOANAMT - DL-INTEREST
          DISPLAY DETAIL-LINE
          PERFORM 200-ADDMONTH UNTIL NUMMONTHS = DL-MONTH

       200-ADDMONTH.
          ADD 1 TO DL-MONTH
          DL-BALANCE = DL-BALANCE - DL-PRINCIPAL
          DL-INTEREST = LOAN * (INTRATE/NUMMONTHS)
          DL-PRINCIPAL = LOANAMT - DL-INTEREST              
          DISPLAY DETAIL-LINE.




       STOP RUN.
months    balance     interest   principal
1        000.00      .75      ,040.71
2        ,959.29     .10      ,048.36
.......
12       ,126.53      .97       ,126.49

首先,解决这个问题。

 months      balance     interest      principal
 01       ,000.00       .75      ,040.71
 02       ,959.29       .10      ,048.36
 .......
 12        ,126.53        .97      ,126.49

看起来更专业,也更容易制作。我不喜欢 "months" 标题,因为不清楚它的意思。一些资本化也很好,但这取决于你。您也可以整理出实际间距。根据我的经验,Principal 总是在 Interest 之前,然后是 Payment 的数字。用户将希望看到付款,而不必计算出来,并希望确认付款的拆分,并直观地验证利息金额。

不过,也许是地区性的。

正如 Brian 在评论中指出的那样,在详细信息行中定义月份时,您已经用肘击中了 9 键。将其设为 PIC 99PIC Z9

您正在将程序编写为 "fall through" 结构。也许这就是您习惯使用其他语言的方式。您将看到的主要是 COBOL 程序将具有不同的结构。

这是您的代码 re-arranged,同样注意缩进,这对人类来说很重要 reader。我发现间距很有用,但不像缩进那样强制:

   PROCEDURE DIVISION.
       PERFORM                     GET-AND-VALIDATE-USER-INPUT
       PERFORM                     PROCESS-USER-INPUT
       PERFORM                     PRODUCE-REPORT
       GOBACK
       .
   GET-AND-VALIDATE-USER-INPUT.
       PERFORM                     GET-AND-VALIDATE-LOAN-AMT
       PERFORM                     GET-AND-VALIDATE-INT-RATE
       PERFORM                     GET-AND-VALIDATE-MONTHS
       .
   GET-AND-VALIDATE-LOAN-AMT.
       DISPLAY "Enter Loan Amount: " WITH NO ADVANCING
       ACCEPT LOANAMT
       IF 0 > LOANAMT
           PERFORM UNTIL LOANAMT > 0
               DISPLAY "Loan Amount must be positive"
               DISPLAY "Enter Loan Amount: " 
                   WITH NO ADVANCING
               ACCEPT LOANAMT
           end-PERFORM
       END-IF           
       .
   GET-AND-VALIDATE-INT-RATE.
       DISPLAY "Enter Annual Interest Rate: " WITH NO ADVANCING
       ACCEPT INTRATE
       IF 0 > INTRATE 
           PERFORM UNTIL INTRATE > 0
               DISPLAY "Annual Interest Rate must be positive"
               DISPLAY "Enter Annual Interest Rate: "  
                   WITH NO ADVANCING
               ACCEPT INTRATE
           end-PERFORM
       END-IF
       .
   GET-AND-VALIDATE-MONTHS.
       DISPLAY "Enter Number of Months: " WITH NO ADVANCING
       ACCEPT NUMMONTHS
       IF 0 > NUMMONTHS
           PERFORM UNTIL NUMMONTHS > 0
               DISPLAY "Number of Months must be positive"
               DISPLAY "Enter Number of Months: " 
                   WITH NO ADVANCING
               ACCEPT NUMMONTHS
           end-PERFORM
       END-IF
       .
   PROCESS-USER-INPUT.
       PERFORM                     GET-AND-VALIDATE-MONTHS

       move LOANAMT                TO LOANFMT
       move INTRATE                TO INTFMT
       MOVE NUMMONTHS              TO MONFMT
       MOVE PMT                    TO PMTFMT
       MOVE TOTPMT                 TO TOTFMT
       .
   PRODUCE-REPORT.
       DISPLAY SPACE     [don't know what you want that for]
       DISPLAY col-hdr
       PERFORM                     FORMAT-INITIAL-LINE
       PERFORM                     OUTPUT-DETAIL-LINE
       PERFORM                     FORMAT-MONTHS-TO-END
       .
   FORMAT-INITIAL-LINE.
       DL-BALANCE                  = LOANAMT
       DL-INTEREST                 = LOAN 
                                   * ( INTRATE 
                                     / NUMMONTHS )
       DL-PRINCIPAL                = LOANAMT 
                                   - DL-INTEREST
       .
   OUTPUT-DETAIL-LINE.
       DISPLAY DETAIL-LINE
       .
   FORMAT-MONTHS-TO-END.
       PERFORM NUMMONTHS = DL-MONTH
           ADD 1                   TO DL-MONTH
           DL-BALANCE              = DL-BALANCE 
                                   - DL-PRINCIPAL
           DL-INTEREST             = LOAN 
                                   * ( INTRATE 
                                     / NUMMONTHS )
           DL-PRINCIPAL            = LOANAMT 
                                   - DL-INTEREST              
           PERFORM                 OUTPUT-DETAIL-LINE
       END-PERFORM
       .

您有作业。 COBOL 没有。 COBOL 有 COMPUTE,因此您需要使用它,尽管 MOVEADDSUBTRACTDIVIDEMULTIPLY 可以澄清为嗯:

   FORMAT-INITIAL-LINE.
       MOVE LOANAMT                TO DL-BALANCE                 
       COMPUTE DL-INTEREST         = LOAN 
                                   * ( INTRATE 
                                     / NUMMONTHS )
       SUBTRACT DL-INTEREST        FROM LOANAMT
         GIVING                    DL-PRINCIPAL
       .

注意 GIVING。 SUBTRACT A FROM B 会改变 B 的值。如果你把 GIVING C 放在最后,B 将不再改变,而是将结果放在 C 中。ADD A TO B 改变 B。ADD A B GIVING C 不会(请注意,这次不需要 TO,尽管从语法上讲它可以存在)。确保您了解 ADD、SUBTRACT、MULTIPLY 和 DIVIDE 的功能。

可能只使用 COMPUTE。与 myth 不同,这里没有性能损失,但是会丢失额外的 human-reader 信息。

使用现代 COBOL 编译器,无需使用任意过程名称(SECTION 或段落)启动程序。它根本没有意义。所以放弃这个(除非由 tutor/site-standards 决定):

   000-MAIN SECTION.

你有这样的事情:

       IF 0 > LOANAMT

并且:

       PERFORM UNTIL LOANAMT > 0

我理解cshneid在评论中提出的观点,但有一致性,而且COBOL没有赋值语句。条件构造中的表达式永远不会导致表达式中涉及的任何字段发生更改。

       IF LOANAMT > 0 

或:

       IF LOANAMT GREATER THAN 0 

神话一般的 COBOL 程序员可以毫不停顿地阅读。

       IF 0 < LOANAMT

更像是一种不连续性。 reader 必须停下来思考这意味着什么。这样做没有好处,也有坏处。

DISPLAYACCEPT 是 COBOL 动词,它们与 COBOL 标准的差异最大,因编译器而异。对于 COBOL 85 标准,ACCEPT 和 DISPLAY 非常简单。您正在使用带有 "Extended" ACCEPT 和 DISPLAY 的编译器。这可能(可能确实)允许输入负数,并可能阻止输入 non-numeric 数据,但您需要查看编译器的文档。输入的数据是数字很重要。得到数字中的字符比不小心输入负值更容易。

来自您的原始代码:

       100-init.
          DL-BALANCE = LOANAMT
          DL-INTEREST = LOAN * (INTRATE/NUMMONTHS)
          DL-PRINCIPAL = LOANAMT - DL-INTEREST
          DISPLAY DETAIL-LINE
          PERFORM 200-ADDMONTH UNTIL NUMMONTHS = DL-MONTH

       200-ADDMONTH.
          ADD 1 TO DL-MONTH
          DL-BALANCE = DL-BALANCE - DL-PRINCIPAL
          DL-INTEREST = LOAN * (INTRATE/NUMMONTHS)
          DL-PRINCIPAL = LOANAMT - DL-INTEREST              
          DISPLAY DETAIL-LINE.

          STOP RUN.

此处,由于未 PERFORM 编辑 100-init,因此程序控制将落入 200-ADDMONTH。标签(段落或章节)只是标签。它们可以是 PERFORM、GO TO 的目标,也可以是 "fallen through" 或 "dropped into"。它们不同于您可能知道的其他语言中的 "subroutine" 或 "function" 定义。

因此,100-init 将执行 200-ADDMONTH 直到完成,然后再次进入 200-ADDMONTH。 从不 故意这样编码。每个 paragraph/SECTION 应该是 self-contained 并且不依赖于其内容的物理位置。

如果 100-init 被执行,你会没事的。有点。因为您在 200-ADDMONTH 中有一个 STOP RUN。 200-ADDMONTH 第一次执行时,程序会停止执行。不是你想要的。

我没有考虑你实际计算的逻辑,只考虑了它的方法。您有重复的代码,因此可以进入另一个 PERFORMed paragraph/SECTION.

在执行时请注意段落和节之间的区别。 SECTION 可以(现在不必)包含段落。当一个 SECTION 被 PERFORMed 时,控制 returns 到下一个 SECTION 之前完成的 PERFORM。当一个段落被 PERFORMed 时,在下一段之前控制 returns。段落不能包含其他段落。要 PERFORM 一系列段落,在 PERFORM 上将需要 THRU。除非由 tutor/site-standards 指示,否则请避免对其进行编码。同样,它依赖于代码的物理位置。这不好。

如今,应该没有对 SECTION 的内在需求,也不需要(除了命令)PERFORM ... THRU ....