Chapter 19 Exercises: Intrinsic Functions Reference

Tier 1: Recall and Recognition (Exercises 1-8)

Exercise 1: FUNCTION Keyword Requirement

Rewrite each of the following incorrect statements to use proper COBOL intrinsic function syntax:

a) COMPUTE WS-RESULT = SQRT(WS-VALUE) b) MOVE UPPER-CASE(WS-NAME) TO WS-UPPER c) DISPLAY LENGTH(WS-FIELD) d) COMPUTE WS-MAX = MAX(WS-A WS-B WS-C)

Answers: a) COMPUTE WS-RESULT = FUNCTION SQRT(WS-VALUE) b) MOVE FUNCTION UPPER-CASE(WS-NAME) TO WS-UPPER c) DISPLAY FUNCTION LENGTH(WS-FIELD) d) COMPUTE WS-MAX = FUNCTION MAX(WS-A WS-B WS-C)

The FUNCTION keyword is mandatory in COBOL before every intrinsic function name. Unlike languages like Python or Java, you cannot simply write the function name and parentheses.

Exercise 2: Function Category Matching

Classify each intrinsic function into its category: Numeric/Math (N), String (S), Date/Time (D), or Financial (F):

Function Category
a) SQRT _
b) UPPER-CASE _
c) CURRENT-DATE _
d) PRESENT-VALUE _
e) LENGTH _
f) INTEGER-OF-DATE _
g) ANNUITY _
h) REVERSE _
i) MOD _
j) TRIM _

Answers: a) N -- numeric/math b) S -- string c) D -- date/time d) F -- financial e) S -- string (returns numeric value, but operates on strings) f) D -- date/time g) F -- financial h) S -- string i) N -- numeric/math j) S -- string

Exercise 3: Function Argument Syntax

Fill in the blanks: In COBOL intrinsic functions, arguments are separated by _ (not _). For example, FUNCTION MAX(A B C) is correct, while FUNCTION MAX(A, B, C) follows the convention of other languages but is ____ in standard COBOL.

Answers: Arguments are separated by spaces (not commas). FUNCTION MAX(A, B, C) is not standard in COBOL -- although some compilers accept commas as separators, the standard uses spaces. Always use spaces for portability.

Exercise 4: Return Type Identification

For each function, identify whether it returns a numeric value, an alphanumeric value, or an integer:

a) FUNCTION LENGTH("HELLO") b) FUNCTION UPPER-CASE("hello") c) FUNCTION SQRT(144) d) FUNCTION CURRENT-DATE e) FUNCTION INTEGER-OF-DATE(20260210) f) FUNCTION TRIM(" HELLO ") g) FUNCTION MOD(17 5) h) FUNCTION ORD("A")

Answers: a) Integer (5) b) Alphanumeric ("HELLO") c) Numeric (12.0) d) Alphanumeric (21-character string: YYYYMMDDHHMMSSssHOOOO) e) Integer (a day number) f) Alphanumeric ("HELLO") g) Integer (2) h) Integer (ordinal position of character in collating sequence)

Exercise 5: CURRENT-DATE Structure

The FUNCTION CURRENT-DATE returns a 21-character string. Identify what each segment represents:

Positions Content
1-4 _
5-6 _
7-8 _
9-10 _
11-12 _
13-14 _
15-16 _
17 _
18-19 _
20-21 _

Answers: | Positions | Content | |-----------|---------| | 1-4 | Year (YYYY) | | 5-6 | Month (MM) | | 7-8 | Day (DD) | | 9-10 | Hour (HH, 24-hour format) | | 11-12 | Minute (MM) | | 13-14 | Second (SS) | | 15-16 | Hundredths of a second (ss) | | 17 | GMT offset sign ('+' or '-' or '0') | | 18-19 | GMT offset hours | | 20-21 | GMT offset minutes |

Exercise 6: Date Function Basics

Write COBOL statements to accomplish each of the following using intrinsic functions:

a) Convert the date 2026-02-10 (as integer 20260210) to an integer day number b) Convert an integer day number back to a YYYYMMDD date c) Calculate the number of days between January 1, 2026 and December 31, 2026 d) Get the day of the week for February 10, 2026

Answers:

      * a) Date to integer day number
           COMPUTE WS-DAY-NUM =
               FUNCTION INTEGER-OF-DATE(20260210)

      * b) Integer day number back to date
           COMPUTE WS-DATE =
               FUNCTION DATE-OF-INTEGER(WS-DAY-NUM)

      * c) Days between two dates
           COMPUTE WS-DAYS-BETWEEN =
               FUNCTION INTEGER-OF-DATE(20261231) -
               FUNCTION INTEGER-OF-DATE(20260101)
      *    Result: 364 days

      * d) Day of week (1=Monday through 7=Sunday)
      *    Calculate integer day number, then MOD 7
           COMPUTE WS-DAY-OF-WEEK =
               FUNCTION MOD(
                   FUNCTION INTEGER-OF-DATE(20260210)
                   7) + 1
      *    Note: The exact result depends on the epoch
      *    used by the implementation. An easier approach:
           ACCEPT WS-DAY-OF-WEEK FROM DAY-OF-WEEK

Exercise 7: String Function Basics

Write COBOL statements using intrinsic functions for each of the following:

a) Convert "john smith" to "JOHN SMITH" b) Convert "JOHN SMITH" to "john smith" c) Get the length of WS-CUSTOMER-NAME d) Reverse the string "ABCDEF" to "FEDCBA" e) Remove leading and trailing spaces from " HELLO "

Answers:

      * a) Convert to uppercase
           MOVE FUNCTION UPPER-CASE("john smith")
               TO WS-UPPER-NAME

      * b) Convert to lowercase
           MOVE FUNCTION LOWER-CASE("JOHN SMITH")
               TO WS-LOWER-NAME

      * c) Get string length
           COMPUTE WS-NAME-LEN =
               FUNCTION LENGTH(WS-CUSTOMER-NAME)

      * d) Reverse a string
           MOVE FUNCTION REVERSE("ABCDEF")
               TO WS-REVERSED
      *    Result: "FEDCBA"

      * e) Trim spaces (COBOL 2014 TRIM function)
           MOVE FUNCTION TRIM("   HELLO   ")
               TO WS-TRIMMED
      *    Result: "HELLO"

Exercise 8: Numeric Function Basics

Write COBOL COMPUTE statements using intrinsic functions:

a) Calculate the square root of 625 b) Find the maximum of three loan amounts: 50000, 75000, 25000 c) Find the minimum of three interest rates: 4.5, 3.75, 5.25 d) Calculate the remainder of 1000000 divided by 7 e) Calculate the absolute value of -5432.10

Answers:

      * a) Square root
           COMPUTE WS-RESULT = FUNCTION SQRT(625)
      *    Result: 25.0

      * b) Maximum value
           COMPUTE WS-MAX-LOAN =
               FUNCTION MAX(50000 75000 25000)
      *    Result: 75000

      * c) Minimum value
           COMPUTE WS-MIN-RATE =
               FUNCTION MIN(4.5 3.75 5.25)
      *    Result: 3.75

      * d) Remainder (modulo)
           COMPUTE WS-REMAINDER =
               FUNCTION MOD(1000000 7)
      *    Result: 6

      * e) Absolute value
           COMPUTE WS-ABS-VAL =
               FUNCTION ABS(-5432.10)
      *    Result: 5432.10

Tier 2: Comprehension and Application (Exercises 9-16)

Exercise 9: Predict the Output

Determine the exact output of each DISPLAY statement:

       01  WS-A    PIC S9(5) VALUE -42.
       01  WS-B    PIC 9(5)  VALUE 16.
       01  WS-C    PIC X(10) VALUE "Hello     ".
       01  WS-D    PIC X(10) VALUE "  World   ".

       PROCEDURE DIVISION.
           DISPLAY FUNCTION ABS(WS-A)
           DISPLAY FUNCTION SQRT(WS-B)
           DISPLAY FUNCTION LENGTH(WS-C)
           DISPLAY FUNCTION LENGTH(
               FUNCTION TRIM(WS-D))
           DISPLAY FUNCTION UPPER-CASE(WS-C)
           DISPLAY FUNCTION REVERSE("ABCDE")
           DISPLAY FUNCTION MAX(10 3 7 15 2)
           DISPLAY FUNCTION MOD(17 5)
           DISPLAY FUNCTION ORD("A")
           DISPLAY FUNCTION CHAR(66)
           STOP RUN.

Answer:

00042                (ABS of -42 = 42, displayed as numeric)
4.0000...            (SQRT of 16 = 4, displayed as floating point)
10                   (LENGTH of WS-C including trailing spaces)
5                    (LENGTH of TRIM("  World   ") = "World" = 5)
HELLO                (UPPER-CASE of "Hello     " = "HELLO     ")
EDCBA                (REVERSE of "ABCDE")
15                   (MAX of 10, 3, 7, 15, 2)
2                    (17 MOD 5 = 2, remainder)
65                   (ORD of "A" in ASCII = 66; in EBCDIC = 194)
B                    (CHAR(66) in ASCII = "B"; EBCDIC differs)

Note: ORD and CHAR results depend on the platform's collating sequence (ASCII vs. EBCDIC). The exact display format for numeric results depends on the receiving field's PIC clause or the DISPLAY behavior.

Exercise 10: Date Arithmetic for Banking

Write a COBOL program segment that uses intrinsic functions to solve each of the following banking date calculations:

a) A certificate of deposit (CD) matures 365 days from today. Calculate the maturity date. b) A loan payment is due on the 15th of each month. Given today's date, calculate how many days until the next payment. c) Calculate the number of business days between two dates (approximate: total days minus weekends, ignoring holidays). d) Determine if the current year is a leap year.

Answer:

      * a) Maturity date = today + 365 days
           MOVE FUNCTION CURRENT-DATE(1:8) TO WS-TODAY
           COMPUTE WS-TODAY-INT =
               FUNCTION INTEGER-OF-DATE(WS-TODAY)
           COMPUTE WS-MATURITY-INT =
               WS-TODAY-INT + 365
           COMPUTE WS-MATURITY-DATE =
               FUNCTION DATE-OF-INTEGER(WS-MATURITY-INT)
           DISPLAY "MATURITY DATE: " WS-MATURITY-DATE

      * b) Days until next payment on the 15th
           MOVE FUNCTION CURRENT-DATE(1:4) TO WS-YEAR
           MOVE FUNCTION CURRENT-DATE(5:2) TO WS-MONTH
           MOVE FUNCTION CURRENT-DATE(7:2) TO WS-DAY
           IF WS-DAY <= 15
               STRING WS-YEAR WS-MONTH "15"
                   DELIMITED BY SIZE INTO WS-NEXT-DUE
           ELSE
               IF WS-MONTH = 12
                   ADD 1 TO WS-YEAR
                   MOVE 01 TO WS-MONTH
               ELSE
                   ADD 1 TO WS-MONTH
               END-IF
               STRING WS-YEAR WS-MONTH "15"
                   DELIMITED BY SIZE INTO WS-NEXT-DUE
           END-IF
           COMPUTE WS-DAYS-UNTIL =
               FUNCTION INTEGER-OF-DATE(WS-NEXT-DUE) -
               FUNCTION INTEGER-OF-DATE(WS-TODAY)

      * c) Approximate business days (no holiday adjustment)
           COMPUTE WS-TOTAL-DAYS =
               FUNCTION INTEGER-OF-DATE(WS-END-DATE) -
               FUNCTION INTEGER-OF-DATE(WS-START-DATE)
           COMPUTE WS-WEEKS = WS-TOTAL-DAYS / 7
           COMPUTE WS-BUS-DAYS =
               WS-TOTAL-DAYS - (WS-WEEKS * 2)

      * d) Leap year check: Feb 29 is valid in leap years
      *    Try INTEGER-OF-DATE for Feb 29 of current year
           MOVE FUNCTION CURRENT-DATE(1:4) TO WS-YEAR
           STRING WS-YEAR "0229"
               DELIMITED BY SIZE INTO WS-TEST-DATE
           COMPUTE WS-TEST-INT =
               FUNCTION INTEGER-OF-DATE(WS-TEST-DATE)
           IF WS-TEST-INT > 0
               DISPLAY WS-YEAR " IS A LEAP YEAR"
           ELSE
               DISPLAY WS-YEAR " IS NOT A LEAP YEAR"
           END-IF

Exercise 11: Financial Functions

Write COBOL statements using the ANNUITY and PRESENT-VALUE intrinsic functions:

a) Calculate the monthly payment on a $250,000 mortgage at 6.5% annual interest for 30 years (360 months). b) Calculate the present value of receiving $1,000 per month for 5 years at 4% annual interest. c) Explain what FUNCTION ANNUITY(rate, periods) returns and how to use it to calculate a loan payment.

Answer:

      * a) Monthly mortgage payment
      *    ANNUITY returns the ratio: payment per $1 of loan
      *    Monthly rate = 6.5% / 12 = 0.005417
           COMPUTE WS-MONTHLY-RATE = 0.065 / 12
           COMPUTE WS-PAYMENT =
               250000 * FUNCTION ANNUITY(
                   WS-MONTHLY-RATE 360)
      *    Result: approximately $1,580.17

      * b) Present value of monthly payments
      *    Monthly rate = 4% / 12 = 0.003333
           COMPUTE WS-PV-RATE = 0.04 / 12
           COMPUTE WS-PRESENT-VALUE =
               FUNCTION PRESENT-VALUE(
                   WS-PV-RATE
                   1000 1000 1000 1000 1000
                   1000 1000 1000 1000 1000
                   1000 1000 1000 1000 1000
                   1000 1000 1000 1000 1000
                   1000 1000 1000 1000 1000
                   1000 1000 1000 1000 1000
                   1000 1000 1000 1000 1000
                   1000 1000 1000 1000 1000
                   1000 1000 1000 1000 1000
                   1000 1000 1000 1000 1000
                   1000 1000 1000 1000 1000
                   1000 1000 1000 1000 1000)
      *    Or use: PV = PMT / ANNUITY(rate, periods)
           COMPUTE WS-PRESENT-VALUE =
               1000 / FUNCTION ANNUITY(WS-PV-RATE 60)

c) FUNCTION ANNUITY(rate, periods) returns the annuity factor: the periodic payment needed to amortize exactly $1 over the given number of periods at the given periodic interest rate. To calculate the actual payment on a loan, multiply the loan amount by the annuity factor:

Payment = Principal * FUNCTION ANNUITY(periodic-rate, number-of-periods)

The periodic rate must match the payment frequency (monthly rate for monthly payments, not the annual rate).

Exercise 12: Nested Function Calls

Write a single COBOL statement that uses nested intrinsic function calls to:

a) Display the length of the trimmed uppercase version of WS-INPUT b) Compute the square root of the absolute value of WS-NEGATIVE-NUM c) Convert today's date to an integer, add 30, and convert back to a date

Answer:

      * a) Nested: LENGTH of TRIM of UPPER-CASE
           DISPLAY FUNCTION LENGTH(
               FUNCTION TRIM(
                   FUNCTION UPPER-CASE(WS-INPUT)))

      * b) Nested: SQRT of ABS
           COMPUTE WS-RESULT =
               FUNCTION SQRT(
                   FUNCTION ABS(WS-NEGATIVE-NUM))

      * c) Nested: DATE-OF-INTEGER of (INTEGER-OF-DATE + 30)
           COMPUTE WS-FUTURE-DATE =
               FUNCTION DATE-OF-INTEGER(
                   FUNCTION INTEGER-OF-DATE(
                       FUNCTION CURRENT-DATE(1:8))
                   + 30)

Functions can be nested to any depth. Each inner function's return value becomes the argument to the outer function. This eliminates the need for intermediate working-storage variables.

Exercise 13: String Functions for Data Cleaning

A bank receives customer data from an external source. The data may have inconsistent formatting. Write a COBOL paragraph that uses intrinsic functions to clean a customer name field:

  1. Remove leading and trailing spaces (TRIM)
  2. Convert to uppercase (UPPER-CASE)
  3. Verify the name is not empty after trimming (LENGTH)
  4. Ensure the name does not exceed 30 characters (LENGTH check)
       2000-CLEAN-CUSTOMER-NAME.
           MOVE FUNCTION TRIM(WS-RAW-NAME)
               TO WS-TRIMMED-NAME

           MOVE FUNCTION UPPER-CASE(WS-TRIMMED-NAME)
               TO WS-CLEAN-NAME

           COMPUTE WS-NAME-LENGTH =
               FUNCTION LENGTH(
                   FUNCTION TRIM(WS-CLEAN-NAME))

           IF WS-NAME-LENGTH = 0
               MOVE "INVALID: BLANK NAME" TO WS-ERROR-MSG
               SET NAME-INVALID TO TRUE
           ELSE
               IF WS-NAME-LENGTH > 30
                   MOVE "INVALID: NAME TOO LONG"
                       TO WS-ERROR-MSG
                   SET NAME-INVALID TO TRUE
               ELSE
                   SET NAME-VALID TO TRUE
               END-IF
           END-IF.

Exercise 14: WHEN-COMPILED Function

Explain what FUNCTION WHEN-COMPILED returns and how it differs from FUNCTION CURRENT-DATE. Write a code snippet that displays both values and explains when each is useful in a banking application.

Answer:

           DISPLAY "COMPILED ON: "
               FUNCTION WHEN-COMPILED
           DISPLAY "RUNNING ON:  "
               FUNCTION CURRENT-DATE

FUNCTION WHEN-COMPILED returns the date and time when the program was compiled. This value is fixed at compile time and never changes, no matter when or how many times the program runs. Format: YYYYMMDDHHMMSSss (16 characters).

FUNCTION CURRENT-DATE returns the current date and time at the moment the function is evaluated. This value changes with every execution.

Banking use cases: - WHEN-COMPILED is useful for audit trails: "This report was generated by program LOANRPT compiled on 2026-01-15 at 14:30:22." This helps identify which version of the program produced a given output. - CURRENT-DATE is useful for timestamps: transaction dates, report dates, audit log entries, and any time-dependent logic (cutoff processing, interest accrual dates).

Exercise 15: INTEGER and INTEGER-PART Functions

Explain the difference between FUNCTION INTEGER, FUNCTION INTEGER-PART, and standard COBOL truncation. What does each produce for the following inputs?

Input INTEGER INTEGER-PART COBOL Truncation (PIC 9(3))
7.8 _ _ _
-7.8 _ _ _
7.2 _ _ _
-7.2 _ _ _

Answers: | Input | INTEGER (floor) | INTEGER-PART (truncate toward 0) | COBOL Truncation | |-------|---------|-------------|----------------------------| | 7.8 | 7 | 7 | 007 | | -7.8 | -8 | -7 | N/A (unsigned) | | 7.2 | 7 | 7 | 007 | | -7.2 | -8 | -7 | N/A (unsigned) |

INTEGER returns the greatest integer not greater than the argument (floor function). For positive numbers, it truncates. For negative numbers, it rounds away from zero: INTEGER(-7.2) = -8.

INTEGER-PART truncates toward zero (removes the decimal part). For both positive and negative numbers, it simply drops the fractional digits: INTEGER-PART(-7.2) = -7.

COBOL truncation (moving to an integer PIC field) truncates toward zero, similar to INTEGER-PART, but only for unsigned fields.

Exercise 16: Statistical Functions

Write COBOL statements to calculate statistical measures for a set of quarterly loan default rates stored in a table:

       01  WS-RATES.
           05  WS-RATE PIC 9V9(4) OCCURS 4.
      *    Values: 0.0234, 0.0187, 0.0312, 0.0256

Calculate: a) The mean (average) default rate b) The median default rate c) The range (max - min) d) The standard deviation (if available)

Answer:

      * a) Mean
           COMPUTE WS-MEAN =
               FUNCTION MEAN(
                   WS-RATE(1) WS-RATE(2)
                   WS-RATE(3) WS-RATE(4))
      *    Result: (0.0234+0.0187+0.0312+0.0256)/4
      *          = 0.0247 (approx)

      * b) Median
           COMPUTE WS-MEDIAN =
               FUNCTION MEDIAN(
                   WS-RATE(1) WS-RATE(2)
                   WS-RATE(3) WS-RATE(4))
      *    Sorted: 0.0187, 0.0234, 0.0256, 0.0312
      *    Median of 4 values = avg of middle 2
      *          = (0.0234 + 0.0256) / 2 = 0.0245

      * c) Range
           COMPUTE WS-RANGE =
               FUNCTION MAX(
                   WS-RATE(1) WS-RATE(2)
                   WS-RATE(3) WS-RATE(4))
             - FUNCTION MIN(
                   WS-RATE(1) WS-RATE(2)
                   WS-RATE(3) WS-RATE(4))
      *    Result: 0.0312 - 0.0187 = 0.0125

      * d) Standard deviation (IBM Enterprise COBOL)
           COMPUTE WS-STDDEV =
               FUNCTION STANDARD-DEVIATION(
                   WS-RATE(1) WS-RATE(2)
                   WS-RATE(3) WS-RATE(4))

Tier 3: Apply (Exercises 17-22)

Exercise 17: Debug These Function Calls

Find and fix the errors in each statement:

a) COMPUTE WS-LEN = LENGTH(WS-NAME) b) MOVE FUNCTION UPPER-CASE WS-NAME TO WS-UPPER c) COMPUTE WS-DAYS = FUNCTION INTEGER-OF-DATE("20260210") d) DISPLAY FUNCTION MAX(WS-A, WS-B, WS-C) e) COMPUTE WS-X = FUNCTION SQRT(-25)

Answers: a) Missing FUNCTION keyword. Fix: COMPUTE WS-LEN = FUNCTION LENGTH(WS-NAME)

b) Missing parentheses around the argument. Fix: MOVE FUNCTION UPPER-CASE(WS-NAME) TO WS-UPPER

c) INTEGER-OF-DATE expects a numeric argument (PIC 9(8)), not a string literal. Fix: COMPUTE WS-DAYS = FUNCTION INTEGER-OF-DATE(20260210) (numeric literal, no quotes)

d) Arguments should be separated by spaces, not commas (though some compilers accept commas). Fix: DISPLAY FUNCTION MAX(WS-A WS-B WS-C)

e) SQRT of a negative number is mathematically undefined and will cause a runtime error. Fix: Take the absolute value first: COMPUTE WS-X = FUNCTION SQRT(FUNCTION ABS(-25))

Exercise 18: Account Number Validation with Functions

Write a COBOL paragraph that validates a 10-digit bank account number using intrinsic functions. The validation rules are:

  1. The account number must be exactly 10 characters (use LENGTH)
  2. After trimming, the account number must still be 10 characters (no embedded spaces)
  3. The first two characters must be uppercase letters (use ORD to check range)
  4. The remaining 8 characters must be numeric (use NUMVAL or manual checking)
  5. The account number check digit (last digit) must be valid (Luhn algorithm)
       3000-VALIDATE-ACCOUNT.
           COMPUTE WS-ACCT-LEN =
               FUNCTION LENGTH(WS-ACCOUNT-NUM)
           IF WS-ACCT-LEN NOT = 10
               MOVE "INVALID LENGTH" TO WS-ERROR-MSG
               SET ACCT-INVALID TO TRUE
               GO TO 3000-EXIT
           END-IF

           COMPUTE WS-TRIMMED-LEN =
               FUNCTION LENGTH(
                   FUNCTION TRIM(WS-ACCOUNT-NUM))
           IF WS-TRIMMED-LEN NOT = 10
               MOVE "EMBEDDED SPACES" TO WS-ERROR-MSG
               SET ACCT-INVALID TO TRUE
               GO TO 3000-EXIT
           END-IF

      *    Check first 2 chars are uppercase A-Z
           COMPUTE WS-ORD-1 =
               FUNCTION ORD(WS-ACCOUNT-NUM(1:1))
           COMPUTE WS-ORD-2 =
               FUNCTION ORD(WS-ACCOUNT-NUM(2:1))
      *    In ASCII: A=65, Z=90
           IF WS-ORD-1 < FUNCTION ORD("A")
              OR WS-ORD-1 > FUNCTION ORD("Z")
              OR WS-ORD-2 < FUNCTION ORD("A")
              OR WS-ORD-2 > FUNCTION ORD("Z")
               MOVE "PREFIX NOT ALPHA" TO WS-ERROR-MSG
               SET ACCT-INVALID TO TRUE
               GO TO 3000-EXIT
           END-IF

      *    Check positions 3-10 are numeric
           IF WS-ACCOUNT-NUM(3:8) IS NOT NUMERIC
               MOVE "SUFFIX NOT NUMERIC" TO WS-ERROR-MSG
               SET ACCT-INVALID TO TRUE
               GO TO 3000-EXIT
           END-IF

           SET ACCT-VALID TO TRUE.

       3000-EXIT.
           EXIT.

Exercise 19: Date Functions for Loan Processing

Write a complete COBOL program that uses intrinsic date functions to calculate loan maturity information. The program reads a loan file and for each loan:

  1. Calculate the maturity date (origination date + term in months)
  2. Calculate days remaining until maturity from today
  3. Determine if the loan is past due (maturity date < today)
  4. Calculate the loan age in years and months
  5. Display all calculated values

Use FUNCTION CURRENT-DATE, FUNCTION INTEGER-OF-DATE, and FUNCTION DATE-OF-INTEGER.

Answer: See code/exercise-solutions.cob (EX19SOL)

Exercise 20: String Functions for Report Formatting

Write a COBOL paragraph that formats a bank customer's name for display on a check or statement. The input fields are separate first name, middle initial, and last name fields. Apply the following transformations using intrinsic functions:

  1. Trim all three name parts
  2. Convert to uppercase
  3. Build the display name as "LAST, FIRST M."
  4. If the total length exceeds 35 characters, truncate the first name
  5. Right-pad the result to exactly 40 characters
       4000-FORMAT-NAME.
           MOVE FUNCTION TRIM(
               FUNCTION UPPER-CASE(WS-LAST-NAME))
               TO WS-CLEAN-LAST

           MOVE FUNCTION TRIM(
               FUNCTION UPPER-CASE(WS-FIRST-NAME))
               TO WS-CLEAN-FIRST

           MOVE FUNCTION TRIM(
               FUNCTION UPPER-CASE(WS-MIDDLE-INIT))
               TO WS-CLEAN-MI

           STRING WS-CLEAN-LAST DELIMITED BY SPACES
                  ", "           DELIMITED BY SIZE
                  WS-CLEAN-FIRST DELIMITED BY SPACES
                  " "            DELIMITED BY SIZE
                  WS-CLEAN-MI   DELIMITED BY SPACES
                  "."            DELIMITED BY SIZE
               INTO WS-DISPLAY-NAME
               WITH POINTER WS-PTR
           END-STRING

      *    Check total length
           COMPUTE WS-NAME-LEN = WS-PTR - 1
           IF WS-NAME-LEN > 35
      *        Truncate at 35 characters
               MOVE WS-DISPLAY-NAME(1:35)
                   TO WS-DISPLAY-NAME
               MOVE 35 TO WS-NAME-LEN
           END-IF

      *    Pad to 40 characters (MOVE pads with spaces)
           MOVE WS-DISPLAY-NAME TO WS-FORMATTED-NAME.

Exercise 21: Financial Calculations with Intrinsic Functions

Write a COBOL program that calculates a complete amortization schedule for the first 12 months of a loan. For each month, use intrinsic functions to calculate:

  1. Monthly payment (using ANNUITY)
  2. Interest portion of payment
  3. Principal portion of payment
  4. Remaining balance
  5. Total interest paid to date
  6. Total principal paid to date

Input: Loan amount = $100,000, Annual rate = 7.5%, Term = 360 months

Answer:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. AMORTIZE.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-PRINCIPAL        PIC S9(9)V99 VALUE 100000.00.
       01  WS-ANNUAL-RATE      PIC 9V9(6)   VALUE 0.075.
       01  WS-MONTHLY-RATE     PIC 9V9(8).
       01  WS-TERM             PIC 9(4)      VALUE 360.
       01  WS-PAYMENT          PIC S9(7)V99.
       01  WS-BALANCE          PIC S9(9)V99.
       01  WS-INTEREST-PMT     PIC S9(7)V99.
       01  WS-PRINCIPAL-PMT    PIC S9(7)V99.
       01  WS-TOTAL-INTEREST   PIC S9(9)V99  VALUE 0.
       01  WS-TOTAL-PRINCIPAL  PIC S9(9)V99  VALUE 0.
       01  WS-MONTH            PIC 9(4).
       01  WS-DISPLAY-BAL      PIC $$$,$$$,$$9.99.
       01  WS-DISPLAY-PMT      PIC $$,$$$,$$9.99.
       01  WS-DISPLAY-INT      PIC $$,$$$,$$9.99.
       01  WS-DISPLAY-PRI      PIC $$,$$$,$$9.99.

       PROCEDURE DIVISION.
       0000-MAIN.
           COMPUTE WS-MONTHLY-RATE =
               WS-ANNUAL-RATE / 12
           COMPUTE WS-PAYMENT =
               WS-PRINCIPAL *
               FUNCTION ANNUITY(WS-MONTHLY-RATE WS-TERM)
           MOVE WS-PRINCIPAL TO WS-BALANCE

           DISPLAY "MONTH   PAYMENT     INTEREST"
               "    PRINCIPAL   BALANCE"
           DISPLAY "-----   ----------  ----------"
               "  ----------  --------------"

           PERFORM VARYING WS-MONTH FROM 1 BY 1
               UNTIL WS-MONTH > 12
               COMPUTE WS-INTEREST-PMT ROUNDED =
                   WS-BALANCE * WS-MONTHLY-RATE
               COMPUTE WS-PRINCIPAL-PMT =
                   WS-PAYMENT - WS-INTEREST-PMT
               SUBTRACT WS-PRINCIPAL-PMT
                   FROM WS-BALANCE
               ADD WS-INTEREST-PMT
                   TO WS-TOTAL-INTEREST
               ADD WS-PRINCIPAL-PMT
                   TO WS-TOTAL-PRINCIPAL
               MOVE WS-PAYMENT TO WS-DISPLAY-PMT
               MOVE WS-INTEREST-PMT TO WS-DISPLAY-INT
               MOVE WS-PRINCIPAL-PMT TO WS-DISPLAY-PRI
               MOVE WS-BALANCE TO WS-DISPLAY-BAL
               DISPLAY WS-MONTH "     "
                   WS-DISPLAY-PMT "  "
                   WS-DISPLAY-INT "  "
                   WS-DISPLAY-PRI "  "
                   WS-DISPLAY-BAL
           END-PERFORM

           DISPLAY " "
           MOVE WS-TOTAL-INTEREST TO WS-DISPLAY-INT
           MOVE WS-TOTAL-PRINCIPAL TO WS-DISPLAY-PRI
           DISPLAY "12-MONTH TOTALS:"
           DISPLAY "  TOTAL INTEREST:  " WS-DISPLAY-INT
           DISPLAY "  TOTAL PRINCIPAL: " WS-DISPLAY-PRI
           STOP RUN.

Exercise 22: Function-Based Data Transformation

Write a COBOL program that reads a file of raw customer data and uses intrinsic functions to transform and clean each record. The transformations are:

  1. Customer name: TRIM + UPPER-CASE
  2. Phone number: extract digits only (use INSPECT and LENGTH)
  3. Date of birth: validate using INTEGER-OF-DATE (returns 0 for invalid dates)
  4. Annual income: calculate monthly income using division, apply ABS to ensure positive
  5. Account age: calculate from opening date to CURRENT-DATE using INTEGER-OF-DATE

For each record, write the cleaned data to an output file and display a count of records with validation errors.

Hint: Some transformations (like extracting digits from a phone number) cannot be done with a single intrinsic function and require INSPECT TALLYING or STRING/UNSTRING in combination with functions.


Tier 4: Analyze (Exercises 23-28)

Exercise 23: Function Performance Analysis

A bank processes 10 million transactions nightly. Each transaction requires calculating the number of days between two dates. Compare the performance of:

Approach A: Using intrinsic functions

           COMPUTE WS-DAYS =
               FUNCTION INTEGER-OF-DATE(WS-END-DATE) -
               FUNCTION INTEGER-OF-DATE(WS-START-DATE)

Approach B: Using a hand-coded date difference subroutine (50 lines of code that manually accounts for month lengths and leap years)

Analyze: code complexity, correctness risk, CPU cost per call, maintainability, and portability. Which approach would you recommend?

Hint: Intrinsic functions are compiled into inline machine code or optimized library calls. They have been thoroughly tested by the compiler vendor across billions of invocations. Hand-coded date routines are custom code that must be individually tested and maintained.

Exercise 24: Cross-Platform Function Availability

A COBOL application is being migrated from IBM Enterprise COBOL (z/OS) to GnuCOBOL (Linux). Identify which of the following functions may have availability or behavior differences:

a) FUNCTION CURRENT-DATE b) FUNCTION UPPER-CASE / LOWER-CASE c) FUNCTION NUMVAL / NUMVAL-C d) FUNCTION ORD / CHAR e) FUNCTION ANNUITY / PRESENT-VALUE f) FUNCTION TRIM g) FUNCTION RANDOM

For each, describe the potential difference and how to handle it during migration.

Hint: The biggest differences are in EBCDIC vs. ASCII character handling (ORD, CHAR, UPPER-CASE) and in the availability of newer COBOL 2014 functions (TRIM, CONCATENATE).

Exercise 25: Replacing Hand-Coded Routines with Functions

The following 45-line paragraph converts a date from YYYYMMDD to a Julian day number. Rewrite it using a single FUNCTION INTEGER-OF-DATE call. Then analyze what bugs might be lurking in the hand-coded version.

       5000-DATE-TO-JULIAN.
           MOVE WS-DATE-YYYY TO WS-YEAR
           MOVE WS-DATE-MM   TO WS-MONTH
           MOVE WS-DATE-DD   TO WS-DAY
           COMPUTE WS-A = (14 - WS-MONTH) / 12
           COMPUTE WS-Y = WS-YEAR + 4800 - WS-A
           COMPUTE WS-M = WS-MONTH + 12 * WS-A - 3
           COMPUTE WS-JULIAN =
               WS-DAY + (153 * WS-M + 2) / 5
               + 365 * WS-Y + WS-Y / 4
               - WS-Y / 100 + WS-Y / 400 - 32045.

Hint: The hand-coded version may have issues with integer truncation in the division steps, edge cases around February 29, and century boundary handling. FUNCTION INTEGER-OF-DATE handles all of these correctly.

Exercise 26: Function Composition Design Patterns

Design a set of reusable COBOL sections (not functions -- COBOL does not have user-defined functions in the traditional sense) that wrap intrinsic function calls for common banking operations. Each section should accept parameters through WORKING-STORAGE and return results through WORKING-STORAGE. Implement:

a) FORMAT-CURRENCY: Accepts a numeric amount and returns a formatted string with currency symbol, commas, and two decimal places b) DAYS-BETWEEN: Accepts two YYYYMMDD dates and returns the number of days between them (handling the case where the end date is before the start date) c) ADD-MONTHS: Accepts a YYYYMMDD date and a number of months, returns the new date (handling month-end edge cases like adding 1 month to January 31)

Hint: ADD-MONTHS is the most complex because month lengths vary. If January 31 + 1 month = February 28 (or 29), you need special handling. One approach: extract year/month, add months, check if the day exceeds the new month's length, and adjust.

Exercise 27: NUMVAL and NUMVAL-C for Financial Input

A bank's ATM network sends transaction amounts as formatted strings like "$1,234.56" and "-$500.00". Write a COBOL paragraph that uses NUMVAL-C to convert these strings to numeric values for processing. Handle the following edge cases:

a) Amounts with dollar sign and commas: "$1,234.56" b) Negative amounts: "-$500.00" or "($500.00)" c) Amounts without formatting: "1234.56" d) Invalid amounts: "N/A", "PENDING", or blank

       6000-PARSE-AMOUNT.
           IF WS-RAW-AMOUNT = SPACES
              OR WS-RAW-AMOUNT = "N/A"
              OR WS-RAW-AMOUNT = "PENDING"
               MOVE 0 TO WS-NUMERIC-AMOUNT
               SET AMOUNT-INVALID TO TRUE
               GO TO 6000-EXIT
           END-IF
      *    NUMVAL-C converts formatted numeric strings
      *    Second argument is the currency sign to strip
           COMPUTE WS-NUMERIC-AMOUNT =
               FUNCTION NUMVAL-C(WS-RAW-AMOUNT "$")
           SET AMOUNT-VALID TO TRUE.

       6000-EXIT.
           EXIT.

Discuss: What happens if NUMVAL-C receives truly invalid data (non-numeric characters other than currency symbols and commas)? How should you protect against this?

Hint: NUMVAL-C will cause a runtime error (or undefined behavior) on truly invalid data. You may need to validate the string before calling NUMVAL-C, or use TEST-NUMVAL-C (COBOL 2014) to check if the string is convertible.

Exercise 28: Function-Based Encryption Seed

A bank needs to generate pseudo-random customer IDs for anonymized test data. Design a scheme using FUNCTION RANDOM and other intrinsic functions:

  1. Seed the random number generator with a value derived from FUNCTION CURRENT-DATE
  2. Generate a random number between 1000000000 and 9999999999 for each customer ID
  3. Ensure no duplicates (or handle them)
  4. Discuss the limitations of FUNCTION RANDOM for this use case

Hint: FUNCTION RANDOM(seed) initializes the random sequence. Subsequent calls to FUNCTION RANDOM (without arguments) return the next pseudo-random number between 0 and 1. To get a specific range: ID = FUNCTION INTEGER(RANDOM * 9000000000) + 1000000000. However, FUNCTION RANDOM is NOT cryptographically secure and should NEVER be used for actual security-sensitive applications.


Tier 5: Create (Exercises 29-33)

Exercise 29: Complete Loan Amortization System

Write a complete COBOL program that generates a full amortization schedule for a bank loan. The program should:

  1. Read loan parameters from an input file (principal, rate, term, start date)
  2. Use FUNCTION ANNUITY to calculate the monthly payment
  3. Use FUNCTION INTEGER-OF-DATE / DATE-OF-INTEGER to calculate each payment date (adding 1 month to the previous date)
  4. For each month, calculate interest, principal, and remaining balance
  5. Handle the final payment adjustment (last payment may differ slightly to zero out the balance)
  6. Produce a formatted report with page headings, detail lines, and summary totals
  7. Calculate and display total interest, total principal, and effective annual rate

Include complete error handling for invalid inputs (negative amounts, zero rates, impossible dates).

Hint: Adding "1 month" to a date is not as simple as adding 30 or 31 days. You must increment the month, handle year boundaries (December to January), and adjust the day if the new month has fewer days (e.g., January 31 + 1 month = February 28).

Exercise 30: Financial Calculator Suite

Write a COBOL program that implements a complete financial calculator using intrinsic functions. The calculator should support:

  1. Loan payment -- given principal, rate, and term, calculate payment (ANNUITY)
  2. Future value -- given payment, rate, and term, calculate the future value of an annuity
  3. Present value -- given payment, rate, and term, calculate present value (PRESENT-VALUE)
  4. Interest rate solver -- given principal, payment, and term, find the rate (iterative approach since there is no direct function)
  5. Term solver -- given principal, payment, and rate, find the number of payments needed
  6. Compound interest -- given principal, rate, and time, calculate the future value with compound interest

The program reads calculation requests from an input file and writes results to a report file.

Hint: The interest rate solver (item 4) requires an iterative approach (Newton-Raphson or bisection) because there is no intrinsic function that directly solves for rate. Start with an estimated rate and converge.

Exercise 31: Date-Driven Batch Scheduler

Write a COBOL program that uses date functions to determine which batch processes should run on a given day. The scheduling rules are:

  1. Daily processes run every day
  2. Weekly processes run on Monday (use INTEGER-OF-DATE MOD 7 to find the day of week)
  3. Monthly processes run on the last business day of each month
  4. Quarterly processes run on the last business day of each quarter
  5. Year-end processes run on December 31 (or the last business day before it)
  6. If a scheduled date falls on a weekend, the process runs on the preceding Friday

The program reads a schedule definition file and today's date (from CURRENT-DATE), and produces a list of processes to execute today.

Hint: Determining the "last business day of the month" requires calculating the last day of the month, checking if it is a Saturday or Sunday, and adjusting backward. The last day of a month can be found by going to the 1st of the next month and subtracting 1 day (using INTEGER-OF-DATE and DATE-OF-INTEGER).

Exercise 32: Data Quality Dashboard

Write a COBOL program that analyzes a customer file and produces a data quality report using intrinsic functions. For each record, assess:

  1. Name quality: non-blank, properly capitalized (first letter uppercase, rest lowercase -- check with UPPER-CASE/LOWER-CASE comparison), no numeric characters
  2. Date quality: all date fields pass INTEGER-OF-DATE validation, dates are within reasonable ranges (not in the future, not before 1900)
  3. Numeric quality: all amount fields are non-negative (ABS check), within reasonable bounds (no balance over $999,999,999)
  4. Completeness: percentage of fields that are non-blank

Produce a summary dashboard showing: - Total records analyzed - Records passing all quality checks - Failure counts by category (name, date, numeric, completeness) - Data quality percentage (passing / total * 100) - Mean and standard deviation of account balances (using MEAN and STANDARD-DEVIATION)

Exercise 33: Multi-Currency Transaction Processor

Write a COBOL program for a bank's international wire transfer system that uses intrinsic functions for:

  1. Currency conversion with precision control (use ROUNDED and explicit rounding modes)
  2. Date calculations for value dates (payment date + 2 business days for international transfers)
  3. String functions for SWIFT message formatting (UPPER-CASE for BIC codes, LENGTH validation for IBAN numbers, TRIM for free-text fields)
  4. Financial calculations for correspondent banking fees (ANNUITY for fee amortization if the fee is spread over multiple transactions)
  5. Random reference number generation (RANDOM seeded with CURRENT-DATE)

Process a file of wire transfer requests and produce: - A processed transactions file with calculated fees and value dates - A SWIFT message file with properly formatted messages - An exception report for failed validations - A daily summary with statistical analysis (MEAN, MEDIAN, STANDARD-DEVIATION of transfer amounts by currency)