CHAPTER 12

image

Advanced Data Declaration

In the last chapter, you learned how to create and use a one-dimensional prefilled table. In this chapter, I continue the discussion of the REDEFINES clause and demonstrate how you can use it to create a two-dimensional prefilled table.

When I introduced the REDEFINES clause in the previous chapter, I did so informally. This chapter formally introduces the REDEFINES clause, including the metalanguage syntax and the semantic rules that apply. It also includes several examples of the other ways in which REDEFINES may be applied.

Because the RENAMES clause is similar to REDEFINES, I introduce RENAMES in this chapter. You learn about the metalanguage syntax for the clause, explore the semantic rules, and see some examples of how to use RENAMES.

The sections discussing the REDEFINES and RENAMES clauses are followed by an introduction to the USAGE clause. I discuss the advantages and disadvantages of USAGE IS DISPLAY (the default). I cover the metalanguage syntax and the semantic rules and examine USAGE IS COMP, USAGE IS PACKED-DECIMAL, and USAGE IS INDEX in more detail. Finally, you learn about the purpose and operation of the SYNCHRONIZED clause.

The final section discusses the importance of fixed-point decimal arithmetic to COBOL’s claim of fitness for creating business or enterprise applications. You learn about the problems with doing financial and commercial calculations using floating-point arithmetic. I discuss the Java BigDecimal class and highlight some of the problems with using it, and I contrast COBOL’s native support for decimal arithmetic with the bolted-on capability provided by BigDecimal.

The Redefines Clause

In the previous chapter, you used the REDEFINES clause to create a prefilled table of values. I noted at the time that the REDEFINES clause has a number of other uses; this chapter discusses how you can use REDEFINES to achieve powerful data-manipulation effects. However, before I discuss the other uses of REDEFINES, let’s revisit its use in creating prefilled tables. Listing 11-4 used the REDEFINES clause to create a table prefilled with the names of the American states. Although it was interesting to see how to create a prefilled table by laying down the values in memory and then using REDEFINES to redefine the area of memory as a table, the mapping between the actual values in memory and the table definition was straightforward because only a one-dimensional table was required. In Listing 12-1, later in this section, you see how to create a prefilled two-dimensional table of values.

Specification: Aromamora Base Oil Sales Report

The following specification defines a program that demonstrates how to create and use a prefilled two-dimensional table of values.

Aromamora PLC is a company that sells essential and base (carrier) oils to aromatherapists, health shops, and other mass users of essential oils. Every month, details of the sales of base oils to these customers are gathered together into a sales file (Listing 12-1.Dat in the code download). A program is required that produces a summary report from this file. The report should show the value of the base oils purchased by each customer and should be printed sequenced on ascending CustomerId.

The Sales File

The sales file contains details of sales to all Aromamora customers. It is a sequential file ordered on ascending CustomerId. The records in the sales file have the following description:

image

Report Template

The report format is given in the template shown in Figure 12-1.

9781430262534_Fig12-01.jpg

Figure 12-1. Report template for Aromamora Summary Sales Report

Notes

Here are some things to consider:

  • The B in the OilId indicates that this is a base oil.
  • UnitSize represents the size of the oil container purchased. There are only three sizes for base oils: 1 (50ml), 2 (100ml), and 3 (200ml).
  • ValueOfSales is the sum of the ValueOfSale calculated for each record.
  • ValueOfSale is UnitsSold * UnitCost(OilNum,Unitsize).
  • The OilName and UnitCost are obtained from a prefilled table of values (see the program outline for details). The two-dimensional table required to hold these values is shown in Figure 12-2.

9781430262534_Fig12-02.jpg

Figure 12-2. Table of oil names and unit costs

Oil Costs Table

Aromamora sells 14 kinds of base oil. The cost of each type of base oil in each of the three container sizes (50ml, 100ml, and 200ml) is given by the table in Figure 12-2. For instance, almond oil costs $02.00 for the 50ml size, $03.50 for 100ml size, and $06.50 for 200ml size.

Example 12-1 demonstrates how you can translate the information given in Figure 12-1 into a prefilled COBOL table. You start by laying down in memory the information you want in the table. Obviously you have to omit the dollar sign and decimal point because those are text and you need to do calculations on the data in the table. At this point, you have a block of undifferentiated data in memory as follows:

Almond          020003500650
Aloe vera       047508501625
Apricot kernel  025004250775
Avocado         027504750875
Coconut         027504750895
Evening primrose037506551225
Grape seed      018503250600
Peanut          027504250795
Jojoba          072513252500
Macadamia       032505751095
Rosehip         052509951850
Sesame          029504250750
Walnut          027504550825
Wheatgerm       045007751425

The final step in creating the table is to use the REDEFINES clause to impose a table definition on the area of memory, as shown in Example 12-1. Once the data is redefined, you can access it using the table. For instance, OilName(9) = Jojoba, and UnitCost(9,2) = 1325.

Example 12-1. Table Definition of the Two-Dimensional Table Shown in Figure 12-1

01  OilsTable.
    02  OilTableValues.
        03 FILLER  PIC X(28) VALUE "Almond          020003500650".
        03 FILLER  PIC X(28) VALUE "Aloe vera       047508501625".
        03 FILLER  PIC X(28) VALUE "Apricot kernel  025004250775".
        03 FILLER  PIC X(28) VALUE "Avocado         027504750875".
        03 FILLER  PIC X(28) VALUE "Coconut         027504750895".
        03 FILLER  PIC X(28) VALUE "Evening primrose037506551225".
        03 FILLER  PIC X(28) VALUE "Grape seed      018503250600".
        03 FILLER  PIC X(28) VALUE "Peanut          027504250795".
        03 FILLER  PIC X(28) VALUE "Jojoba          072513252500".
        03 FILLER  PIC X(28) VALUE "Macadamia       032505751095".
        03 FILLER  PIC X(28) VALUE "Rosehip         052509951850".
        03 FILLER  PIC X(28) VALUE "Sesame          029504250750".
        03 FILLER  PIC X(28) VALUE "Walnut          027504550825".
        03 FILLER  PIC X(28) VALUE "Wheatgerm       045007751425".
    02  FILLER REDEFINES OilTableValues.
        03 BaseOil OCCURS 14 TIMES.
           04 OilName    PIC X(16).
           04 UnitCost   PIC 99V99 OCCURS 3 TIMES.

Program

This is a typical one-level control-break program. I have kept the program simple (see Listing 12-1) to allow you to focus on the declaration and use of the two-dimensional table. Note that in a real situation, the oil-cost table would not be static as it is in this program. The cost data is likely to change, so for maintenance reasons the table would probably be instantiated from a file. A portion of the sales file used to the test the program and the summary report produced from that file are shown in Figure 12-3 in the next section.

Listing 12-1. Aromamora Base Oils Summary Sales Report

IDENTIFICATION DIVISION.
PROGRAM-ID. Listing12-1.
AUTHOR.  Michael Coughlan.
* This program produces a summary report showing the sales of base oils
* to Aromamora customers by processing the OilSalesFile.  The OilSalesFile is a
* sequential file ordered on ascending CustomerId.  The report is required to be
* printed in ascending CustomerId order.
 
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
       SELECT BaseOilsSalesFile ASSIGN TO "Listing12-1.Dat"
                 ORGANIZATION IS LINE SEQUENTIAL.
 
       SELECT SummaryReport ASSIGN TO "Listing12-1.Rpt"
                 ORGANIZATION IS LINE SEQUENTIAL.
                  
DATA DIVISION.
FILE SECTION.
FD  BaseOilsSalesFile.
01  SalesRec.
    88  EndOfSalesFile         VALUE HIGH-VALUES.
    02  CustomerId             PIC X(5).
    02  CustomerName           PIC X(20).
    02  OilId.
        03 FILLER              PIC X.
        03 OilNum              PIC 99.
    02  UnitSize               PIC 9.
    02  UnitsSold              PIC 999.
 
FD SummaryReport.
01 PrintLine                   PIC X(45).
 
WORKING-STORAGE SECTION.
01  OilsTable.
    02  OilTableValues.
        03 FILLER  PIC X(28) VALUE "Almond          020003500650".
        03 FILLER  PIC X(28) VALUE "Aloe vera       047508501625".
        03 FILLER  PIC X(28) VALUE "Apricot kernel  025004250775".
        03 FILLER  PIC X(28) VALUE "Avocado         027504750875".
        03 FILLER  PIC X(28) VALUE "Coconut         027504750895".
        03 FILLER  PIC X(28) VALUE "Evening primrose037506551225".
        03 FILLER  PIC X(28) VALUE "Grape seed      018503250600".
        03 FILLER  PIC X(28) VALUE "Peanut          027504250795".
        03 FILLER  PIC X(28) VALUE "Jojoba          072513252500".
        03 FILLER  PIC X(28) VALUE "Macadamia       032505751095".
        03 FILLER  PIC X(28) VALUE "Rosehip         052509951850".
        03 FILLER  PIC X(28) VALUE "Sesame          029504250750".
        03 FILLER  PIC X(28) VALUE "Walnut          027504550825".
        03 FILLER  PIC X(28) VALUE "Wheatgerm       045007751425".
    02  FILLER REDEFINES OilTableValues.
        03 BaseOil OCCURS 14 TIMES.
           04 OilName    PIC X(16).
           04 UnitCost   PIC 99V99 OCCURS 3 TIMES.
 
01  ReportHeadingLine      PIC X(41)
            VALUE " Aromamora Base Oils Summary Sales Report".
 
01  TopicHeadingLine.
    02  FILLER             PIC X(9)  VALUE "Cust Id".
    02  FILLER             PIC X(15) VALUE "Customer Name".
    02  FILLER             PIC X(7)  VALUE SPACES.
    02  FILLER             PIC X(12) VALUE "ValueOfSales".
    
01  ReportFooterLine       PIC X(43)
            VALUE "************** End of Report **************".
 
01  CustSalesLine.
    02  PrnCustId          PIC B9(5).
    02  PrnCustName        PIC BBBX(20).
    02  PrnCustTotalSales  PIC BBB$$$$,$$9.99.
 
01  CustTotalSales         PIC 9(6)V99.
01  PrevCustId             PIC X(5).
01  ValueOfSale            PIC 9(5)V99.
 
PROCEDURE DIVISION.
Print-Summary-Report.
    OPEN OUTPUT SummaryReport
    OPEN INPUT BaseOilsSalesFile
    
    WRITE PrintLine FROM ReportHeadingLine AFTER ADVANCING 1 LINE
    WRITE PrintLine FROM TopicHeadingLine  AFTER ADVANCING 2 LINES
 
    READ BaseOilsSalesFile
        AT END SET EndOfSalesFile TO TRUE
    END-Read
 
    PERFORM PrintCustomerLines UNTIL EndOfSalesFile
    
    WRITE PrintLine FROM ReportFooterLine AFTER ADVANCING 3 LINES
 
    CLOSE SummaryReport, BaseOilsSalesFile
    STOP RUN.
 
PrintCustomerLines.
    MOVE ZEROS TO CustTotalSales
    MOVE CustomerId TO PrnCustId, PrevCustId
    MOVE CustomerName TO PrnCustName
 
    PERFORM UNTIL CustomerId NOT = PrevCustId
        COMPUTE ValueOfSale = UnitsSold * UnitCost(OilNum, UnitSize)
        ADD ValueOfSale TO CustTotalSales
        READ BaseOilsSalesFile
           AT END SET EndOfSalesFile TO TRUE
        END-Read
    END-PERFORM
 
    MOVE CustTotalSales TO PrnCustTotalSales
    WRITE PrintLine FROM CustSalesLine AFTER ADVANCING 2 LINES.

Test Data and Results

Due to space constraints, only a portion of the test data file is shown (see Figure 12-3).

9781430262534_Fig12-03.jpg

Figure 12-3. Partial test data and results produced

The REDEFINES Clause

So far, I have dealt informally with the REDEFINES clause. You have seen how to use it to create a prefilled table of values, but I have not formally defined what REDEFINES does or explored its other uses.

When a file contains different types of records, you must create a separate record description for each record type in the file’s FD entry. You have seen that all these record descriptions map on to the same area of storage. They are, in effect, redefinitions of the area of storage. What the REDEFINES clause allows you to do is to achieve the same effect for units smaller than a record and in parts of the DATA DIVISION other than the FILE SECTION. The REDEFINES clause lets you give different data descriptions to the same area of storage.

REDEFINES Syntax

The syntax metalanguage for the REDEFINES clause is given in Figure 12-4. Identifier1 is the data item that originally defines the area of storage, and Identifier2 is the data item that redefines it.

9781430262534_Fig12-04.jpg

Figure 12-4. Syntax metalanguage for the REDEFINES clause

REDEFINES Notes

The metalanguage defines the syntax of the REDEFINES clause, but there are also a number of semantic rules that must be obeyed when you use REDEFINES:

  • The REDEFINES clause must immediately follow Identifier2 (that is, REDEFINES must come before PIC [see Example 12-2]).
  • The level numbers of Identifier1 and Identifier2 must be the same and cannot be 66 or 88.
  • The data description of Identifier1 cannot contain an OCCURS clause (that is, you can’t redefine a table element).
  • If there are multiple redefinitions of the same area of storage, then they must all redefine the data item that originally defined the area (see Example 12-5).
  • The redefining entries (Identifier2) cannot contain VALUE clauses except in condition name entries.
  • No entry with a level number lower (that is, higher in the hierarchy) than the level number of Identifier1 and Identifier2 can occur between Identifier1 and Identifier2.
  • Entries redefining the area must immediately follow those that originally defined it.
  • Only entries subordinate to Identifier1 are allowed between Identifier1 and Identifier2.
  • The REDEFINES clause must not be used for records (01 level) described in the FILE SECTION because multiple 01 entries for the same file are implicit redefinitions of the first 01 level record.

REDEFINES Examples

The best way to understand how the REDEFINES clause works is to explore some of the ways it may be used through a number of examples.

REDEFINES Example 1

Some COBOL statements, such as UNSTRING, require their receiving fields to be alphanumeric (PIC X) data items. This is inconvenient if the value of the data item is actually numeric, because then a MOVE is required to place the value into a numeric item. If the value contains a decimal point, this creates even more difficulties.

For example, suppose an UNSTRING statement has just extracted the text value “5432195” from a string, and you want to move this value to a numeric item described as PIC 9(5)V99. An ordinary MOVE is not going to work because the computer will not know that you want the item treated as if it were the value 654321.95.

The REDEFINES clause allows you to solve this problem neatly because you can UNSTRING the number into TextValue and then treat TextValue as if it were described as PIC 9(5)V99 (see Example 12-2). If TextValue contains the alphanumeric value “65432195”, then NumericValue, which REDEFINES it, sees the value as 654321.95 (see Figure 12-5).

Example 12-2. Redefining an Alphanumeric Item as a Decimal Data Item

01 RedefinesExample1.
   02 TextValue    PIC X(8).
   02 NumericValue REDEFINES TextValue PIC 9(6)V99.

9781430262534_Fig12-05.jpg

Figure 12-5. Memory model showing the result of redefinition

REDEFINES Example 2

The first example showed how you can use the REDEFINES clause to treat a set of alphanumeric digits as a decimal number. This example explores a similar problem. When a program ACCEPTs a decimal number from a user, the decimal point is included. This is a problem because this decimal point is a text character. If you move a numeric literal (such as 1234.55) that contains a decimal point into a numeric data item that contains an assumed decimal point (such as PIC 9(5)V99), the actual and assumed decimal points align. This does not happen when you move an item containing the decimal point text character. In fact, if you move an item containing an actual decimal point into a numeric data item and then try to perform an arithmetic calculation on that data item, the program will crash (halt unexpectedly).

A solution to this problem is given in Example 12-3. When a number containing an actual decimal point is accepted from the user, the UNSTRING verb is used to split the input string into the digits before the decimal point and those after the decimal point. Although WorkArea contains only numeric digits, because it is a group item, its type is alphanumeric, and so it can’t be used in a calculation. The solution is to redefine WorkArea as WorkNum, which is a numeric data item that can be used in calculations. A model of the redefined data items is given in Figure 12-6.

Example 12-3. Redefining Two Data Items as a Single Numeric Item

WORKING-STORAGE SECTION.
01  InputString    PIC X(8).
 
01  WorkArea.
    02 Fnum     PIC 9(5) VALUE ZEROS.
    02 Snum     PIC 99   VALUE ZEROS.
01  WorkNum REDEFINES WorkArea PIC 99999V99.
 
01 EditedNum   PIC ZZ,ZZ9.99.
 
PROCEDURE DIVISION.
Begin.
    DISPLAY "Enter a decimal number - " WITH NO ADVANCING
    ACCEPT InputString
    UNSTRING InputString DELIMITED BY ".", ALL SPACES
        INTO Fnum, Snum
    MOVE WorkNum TO EditedNum
    DISPLAY "Decimal Number = " EditedNum
    ADD 10 TO WorkNum
    MOVE WorkNum TO EditedNum
    DISPLAY "Decimal Number = " EditedNum

9781430262534_unFig12-01.jpg

9781430262534_Fig12-06.jpg

Figure 12-6. Model showing WorkArea, Fnum, and Snum redefined as WorkNum

REDEFINES Example 3

Working with percentages often presents a problem. If the percentage is held as an integer, then calculations are complicated by having to divide by 100. For instance, COMPUTE PercentOfBase = BaseAmount * PercentToApply /100.

On the other hand, if the percentage is held as a decimal fraction, then calculations are made simpler but communication with users is complicated because now they have to input or print the percentage as a decimal fraction rather than a whole number.

The solution is to take in the percentage as an integer value and then use REDEFINES to treat it as a decimal fraction. Example 12-4 is a program fragment that shows how this works.

Example 12-4. Using REDEFINES to Allow Different Views of a Percentage Value

DATA DIVISION.
WORKING-STORAGE SECTION.
01  PercentToApply    PIC 9(3).
01  Percentage REDEFINES PercentToApply PIC 9V99.
                  
01  BaseAmount        PIC 9(5) VALUE 10555.
01  PercentOfBase     PIC ZZ,ZZ9.99.
01  PrnPercent        PIC ZZ9.
 
PROCEDURE DIVISION.
Begin.
   MOVE 23 TO PercentToApply
   COMPUTE PercentOfBase = BaseAmount * Percentage
   DISPLAY "23% of 10555 is = " PercentOfBase
   MOVE PercentToApply to PrnPercent
   DISPLAY "Percentage applied was " PrnPercent "%"
   STOP RUN.

9781430262534_unFig12-02.jpg

REDEFINES Example 4

The REDEFINES clause is also useful when you need to treat a numeric item as if it had its decimal point in a different place. For instance, Example 12-5 shows how you can use the REDEFINES clause to provide time conversions between seconds, milliseconds, microseconds, and nanoseconds.

The main purpose of Example 12-5 is to illustrate the rule that if there are multiple redefinitions of an area of storage, they must all refer to the data item that originally defined the area of storage.

Example 12-5. Time Conversion by Multiple Redefinition

WORKING-STORAGE SECTION.
01 NanoSecs     PIC 9(10).
01 MicroSecs    REDEFINES NanoSecs PIC 9999999V999.
01 MilliSecs    REDEFINES NanoSecs PIC 9999V999999.
01 Seconds      REDEFINES NanoSecs PIC 9V999999999.
 
01 EditedNum    PIC Z,ZZZ,ZZZ,ZZ9.99.
 
PROCEDURE DIVISION.
Begin.
   MOVE 1234567895 TO NanoSecs
   MOVE NanoSecs TO EditedNum
   DISPLAY EditedNum " NanoSecs"
 
   MOVE MicroSecs TO EditedNum
   DISPLAY EditedNum " MicroSecs"
    
   MOVE MilliSecs TO EditedNum
   DISPLAY EditedNum " MilliSecs"
      
   MOVE Seconds TO EditedNum
   DISPLAY EditedNum " Seconds"
   STOP RUN.

9781430262534_unFig12-03.jpg

The RENAMES Clause

As you have seen, the REDEFINES clause allows you to give a new data definition and name to an area of storage. The RENAMES clause lets you give a new name (or alias) to a data item or a collection of data items. This can be useful when you want to regroup a number of elementary data items in a record so that they can belong to the original as well as to the new group.

The RENAMES clause is used with the special level number 66. In the same way that condition names are sometimes called level eighty-eights, RENAMES data items are sometimes called level sixty-sixes.

Because of the maintenance problems associated with RENAMES , it has largely fallen into disuse; and in some programming shops, it is banned. I include it here only for completeness.

RENAMES Syntax

The syntax metalanguage for the RENAMES clause is given in Figure 12-7. Identifier2 [THRU Identifier3] is the original area of storage, and Identifier1 is the new name that you can use to manipulate it.

9781430262534_Fig12-07.jpg

Figure 12-7. RENAMES metalanguage

RENAMES Notes

The syntax diagram in Figure 12-7 is modified by the following semantic rules:

  • The level number of Identifier2 and Identifier3 cannot be 77, 88, 01, or 66.
  • Identifier2 and Identifier3 must not contain an OCCURS clause or be subordinate to a data item that contains an OCCURS clause.
  • No data item between Identifier2 and Identifier3 can contain an OCCURS clause.
  • RENAMES entries must follow the last data-description entry of a record (can’t be in the middle of a record description).

RENAMES Examples

Listing 12-2 contains a number of examples that show how to use the RENAMES clause.

Listing 12-2. RENAMES Examples

IDENTIFICATION DIVISION.
PROGRAM-ID. Listing12-2
AUTHOR. Michael Coughlan.
* RENAMES clause examples
DATA DIVISION.
WORKING-STORAGE SECTION.
01 StudentRec.
   02 StudentId        PIC 9(8)  VALUE 12345678.
   02 GPA              PIC 9V99  VALUE 3.25.
   02 ForeName         PIC X(6)  VALUE "Matt".
   02 SurName          PIC X(8)  VALUE "Cullen".
   02 Gender           PIC X     VALUE "M".
   02 PhoneNumber      PIC X(14) VALUE "3536120228233".
 
66 PersonalInfo RENAMES ForeName  THRU PhoneNumber.
66 CollegeInfo  RENAMES StudentId THRU SurName.
66 StudentName  RENAMES ForeName  THRU SurName.
    
01 ContactInfo.
   02 StudName.
      03 StudForename  PIC X(6).
      03 StudSurname   PIC X(8).
   02 StudGender       PIC X.
   02 StudPhone        PIC X(14).
    
66 MyPhone RENAMES StudPhone.
 
PROCEDURE DIVISION.
Begin.
   DISPLAY "Example 1"
   DISPLAY "All information = " StudentRec
   DISPLAY "College info    = " CollegeInfo
   DISPLAY "Personal Info   = " PersonalInfo
 
   DISPLAY "Example 2"
   DISPLAY "Combined names  = " StudentName
    
   MOVE PersonalInfo TO ContactInfo
    
   DISPLAY "Example 3"
   DISPLAY "Name    is " StudName
   DISPLAY "Gender  is " StudGender
   DISPLAY "Phone   is " StudPhone
    
   DISPLAY "Example 4"
   DISPLAY "MyPhone is " MyPhone
   STOP RUN.

9781430262534_unFig12-04.jpg

Listing Notes

Listing 12-2 contains a number of RENAMES clause examples. The first example uses the RENAMES clause to rename sections of the StudentRec to allow the college and personal information parts of the record to be accessed separately. No new data storage is created when this is done, but the existing storage is given new names. This example also shows that multiple, overlapping, RENAMES may be used.

The second example renames the elementary data items ForeName and SurName as StudentName so they can be treated as a single item (that is, members of a group item). For the purpose of contrast, in the record ContactInfo I made these items subordinate to a group item.

The third example shows that the renamed data can be manipulated using the new name. I moved PersonalInfo to ContactInfo and then displayed the individual fields in ContactInfo.

The final example shows that you can also use RENAMES to rename a single item.

The USAGE Clause

Computers store their data in the form of binary digits. Apart from cardinal numbers (positive integers), all other data stored in the computer’s memory uses some sort of formatting convention.

For instance, text data is stored using an encoding sequence like ASCII or EBCDIC. An encoding system is simply a convention that specifies that a particular set of bits represents a particular character. For instance, Figure 12-8 shows the bit configuration used to represent an uppercase A in the ASCII and EBCDIC encoding sequences.

9781430262534_Fig12-08.jpg

Figure 12-8. Uppercase A in the ASCII and EBCDIC encoding sequences

Representation of Numeric Data

COBOL gives you a lot of control over how numeric data is held in memory. In COBOL, numeric data can be held as text digits (ASCII digits), as twos-complement binary numbers, or as decimal numbers (using binary-coded decimal [BCD]).

You use the USAGE clause to specify how a data item is to be stored in the computer’s memory. Every data item declared in a COBOL program has a USAGE clause—even when no explicit clause is specified. When there is no explicit USAGE clause, the default USAGE IS DISPLAY is applied. USAGE IS DISPLAY has been used in all the examples so far.

Disadvantage of USAGE DISPLAY

For text items, or for numeric items that will not be used in a computation (phone numbers, account numbers, and so son), the default of USAGE IS DISPLAY presents no problems. But the default usage is not the most efficient way to store data that will be used in a calculation.

When numeric items (PIC 9 items) have a usage of DISPLAY, they are stored as ASCII digits (see the ASCII digits 0–9 in the ASCII table in Figure 12-9).

9781430262534_Fig12-09.jpg

Figure 12-9. Table of ASCII digits

Consider the program fragment in Example 12-6. Figure 12-10 shows what would happen if computations were done directly on numbers stored in this format. Because none of the data items have an explicit USAGE clause, they default to USAGE IS DISPLAY. This means the values in the variables Num1, Num2, and Num3 are stored as ASCII digits. This in turn means the digit 4 in Num1 is encoded as 00110100 and the digit 1 in Num2 is encoded as 00110001. When these binary numbers are added together, the result, as shown in Figure 12-10, is the binary value 01100101, which is the ASCII code for the lowercase letter e.

Example 12-6. Arithmetic on Items Held as USAGE IS DISPLAY

Num1   PIC 9 VALUE 4.
Num2   PIC 9 VALUE 1.
Num3   PIC 9 VALUE ZERO.
:  :  :  :  :  :  :  :  :
ADD Num1, Num2 GIVING Num3.

9781430262534_Fig12-10.jpg

Figure 12-10. Adding two ASCII digits gives the wrong result

When calculations are done with numeric data items using USAGE IS DISPLAY, the computer has to convert the numeric values to their binary equivalents before the calculation can be done. When the result has been computed, the computer must reconvert it to ASCII digits. Conversion to and from ASCII digits slows down computations.

For this reason, data that is heavily involved in computation is often declared using one of the usages optimized for computation, such as USAGE IS COMPUTATIONAL.

Advantage of USAGE IS DISPLAY

Although it is computationally inefficient, there are a number of advantages to holding numeric data as text digits. One obvious advantage is that USAGE DISPLAY items can be output to the computer screen by the DISPLAY verb without the need for conversion. Another advantage is portability. Files whose data is encoded as text can be processed without difficulty on different makes of computers or using other programming languages. In contrast, the chosen binary formats of some computers and the Big Endian/Little Endian byte order preference of others means that non-text files produced on one make of computer are often difficult to read on another make of computer or even using another programming language or utility program on the same computer.

STORAGE OF MULTIBYTE NUMBERS

Some computers store numeric binary values using a byte order where the low-order byte of a number is stored at the lowest memory address, and the high-order byte at the highest address. This is known as the Little Endian byte order. For instance, a four-byte-long integer value would be stored as

Byte0 at BaseAddress+0
Byte1 at BaseAddress+1
Byte2 at BaseAddress+2
Byte3 at BaseAddress+3

In contrast, in Big Endian computers, the high-order byte of the number is stored at the lowest memory address, and the low-order byte at the highest address. For instance:

Byte3 at BaseAddress +0
Byte2 at BaseAddress +1
Byte1 at BaseAddress +2
Byte0 at BaseAddress +3

The advantages of the USAGE IS DISPLAY format and the speed of modern computers means that the USAGE clause is an optimization that is only worth doing if the data item will be used in thousands of computations.

USAGE Clause Syntax

As you have seen, the default representation (USAGE IS DISPLAY) used by COBOL for numeric data items can negatively impact the speed of computations. USAGE is used for purposes of optimization of both speed and storage. It allows you to control the way data items (normally numeric data items) are stored in memory. One important point to note is that because computers can be quite different under the skin (for instance, register size and Endian order), the COBOL standard leaves the actual implementation of the binary data items to the compiler implementer. This means a COMP item on one computer may not be exactly the same as a COMP item on another computer.

The metalanguage syntax diagram for the USAGE clause is given in Figure 12-11, and some example declarations are shown in Example 12-7.

9781430262534_Fig12-11.jpg

Figure 12-11. The USAGE clause metalanguage

Example 12-7. Example USAGE Clause Declarations

01 Num1             PIC 9(5)V99  USAGE IS COMP.
01 Num2             PIC 99       USAGE IS PACKED-DECIMAL.
01 IdxItem     USAGE IS INDEX.
 
01 FirstGroup    COMP.
   02 Item1      PIC 999.
   02 Item2      PIC 9(4)V99.
   02 Item3      PIC S9(5) COMP SYNC.

Notes

Here are some things to note:

  • The USAGE clause may be used with any data description entry except those with level numbers of 66 or 88.
  • When the USAGE clause is declared for a group item, the usage specified is applied to every item in the group. The group item itself is still treated as an alphanumeric data item (see FirstGroup in Example 12-7).
  • The USAGE clause of an elementary item cannot override the USAGE clause of the group to which it is subordinate (for instance, in Example 12-7, the USAGE of Item3 is COMP because that is the USAGE of FirstGroup).
  • USAGE IS COMPUTATIONAL and COMP or BINARY are synonyms of one another.
  • The USAGE IS INDEX clause is used to provide an optimized table subscript.
  • Any item declared with USAGE IS INDEX can only appear in
    • A SEARCH or SET statement
    • A relation condition
    • The USING phrase of the PROCEDURE DIVISION
    • The USING phrase of the CALL statement
  • The picture string of a COMP or PACKED-DECIMAL item can contain only the symbols 9, S, V, and/or P.
  • The picture clause used for COMP and PACKED-DECIMAL items must be numeric.

image Bug Alert  Group items are always treated as alphanumeric, and this can cause problems when there are subordinate COMP items. For instance, suppose you defined a group of data items as follows:

01 SecondGroup.
   02 NumItem1   PIC 9(3)V99 USAGE IS COMP.
   02 NumItem2   PIC 99V99   USAGE IS COMP.

and then applied a statement such as MOVE ZEROS TO SecondGroup to it.

On the surface, it appears that the statement is moving the numeric value 0 to NumItem1 and NumItem2; but because SecondGroup is an alphanumeric item, what is actually moved into NumItem1 and NumItem2 is the ASCII digit “0”. When an attempt is made to use NumItem1 or NumItem2 in a calculation, the program will crash because these data items contain non-numeric data.

COMP Explanation

COMP items are held in memory as pure binary twos-complement numbers. You don’t have to understand how twos-complement numbers work or how they are stored, but you must understand the storage requirements for fields described as COMP. The storage requirements are shown in Table 12-1. For instance, the declaration

01 TotalCount   PIC 9(7) USAGE IS COMP

requires a LongWord (4 bytes) of storage.

Table 12-1. Storage Requirements of  COMP Data Items

Number of Digits

Storage Required

PIC 9(1 to 4)

1 Word (2 bytes)

PIC 9(5 to 9)

1 LongWord (4 bytes)

PIC 9(10 to 18)

1 QuadWord (8 bytes)

PACKED-DECIMAL Explanation

Data items declared as PACKED-DECIMALare held in binary-coded decimal (BCD) form. Instead of representing the value as a single binary number, the binary value of each digit is held in a nibble (half a byte). The sign is held in a separate nibble in the least significant position of the item (see Figure 12-12).

9781430262534_Fig12-12.jpg

Figure 12-12. Memory representation of BCD numbers

The SYNCHRONIZED Clause

The SYNCHRONIZED clause is sometimes used with USAGE IS COMP or USAGE IS INDEX items. It is used to optimize speed of processing, but it does so at the expense of increased storage requirements.

Many computer memories are organized in such a way that there are natural addressing boundaries, such as word boundaries. If no special action is taken, some data items in memory may straddle these boundaries. This may cause processing overhead because the CPU may need two fetch cycles to retrieve the data from memory. The SYNCHRONIZED clause is used to explicitly align COMP and INDEX items along their natural word boundaries. Without SYNCHRONIZED, data items are aligned on byte boundaries. The metalanguage for SYNCHRONIZED is given in Figure 12-13.

9781430262534_Fig12-13.jpg

Figure 12-13. Metalanguage for the SYNCHRONIZED clause

The effect of the SYNCHRONIZED clause is implementation dependent. You need to read your implementer manual to see how it works on your computer (in some cases it may have no effect). To illustrate how SYNCHRONIZED works in general, assume that a COBOL program is running on a word-oriented computer where the CPU fetches data from memory a word at a time. Suppose the program performs a calculation on the number stored in the variable TwoBytes (as shown in Figure 12-14). Because of the way the data items have been declared, the number stored in TwoBytes straddles a word boundary. In order to use the number, the CPU has to execute two fetch cycles: one to get the first part of the number in Word2 and the second to get the second part of the number in Word3. This double fetch slows down calculations.

9781430262534_Fig12-14.jpg

Figure 12-14. With no SYNCHRONIZED clause, numbers may straddle word boundaries

When the SYNCHRONIZED clause is used, as shown in Figure 12-15, TwoBytes is aligned along the word boundary, so the CPU only has to do one fetch cycle to retrieve the number from memory. This speeds up processing, but at the expense of wasting some storage (the second byte of Word2 is no longer used).

9781430262534_Fig12-15.jpg

Figure 12-15. With the SYNCHRONIZED, clause numbers are aligned along word boundaries

Nonstandard USAGE Extensions

The USAGE clause is one of the areas where many implementers have introduced extensions to the COBOL standard. It is not uncommon to see COMP-1, COMP-2, COMP-3, COMP-4, COMP-5, and POINTER usage items in programs written using these extensions.

Even though COMP-1 and COMP-2 are extensions to the COBOL standard, many implementers seem to use identical representations for these usages. Comp-1 is usually defined as a single-precision, floating-point number, adhering to the IEEE specification for such numbers (Real or float in typed languages). Comp-2 is usually defined as an IEEE double-precision, floating-point number (LongReal or double in typed languages). COMP-3 items are usually defined as BCD numbers. The official introduction of PACKED-DECIMAL in the ANS 85 version of COBOL has made this extension unnecessary.

Decimal Arithmetic

One of COBOL’s strengths, and one of its main claims to fitness for writing business and enterprise applications, is its native support for fixed-point decimal arithmetic. Until the problems associated with floating-point arithmetic are pointed out to them, most programmers are not even aware that there is a problem. In the Java community, this is such a problem that even now articles and forum posts are still produced warning of the dangers of using float or double for currency calculations.

The problem with floating-point arithmetic is this: binary floating-point numbers (such as those with a type of real, float, or double) cannot represent common decimal fractions exactly. For instance, common decimal fractions like 0.1 do not have a terminating binary representation. Just as 1/3 is a repeating fraction in decimal, 1/10 is a repeating fraction in binary. As a result, floating-point numbers can’t be used safely for financial calculations. In fact, they cannot be used for any calculations where the result produced is required to match those that might be calculated by hand.

In an article on the Java Performance Tuning Guide1, Mikhail Vorontsov emphasizes this point when he notes that double calculations are not precise even for simple operations such as addition and subtraction. For instance, he notes that the Java statement

System.out.println( "362.2 - 362.6 = " + ( 362.2 - 362.6 ) );

produces the output

362.2 - 362.6 = -0.4000000000000341

The advantage of doing computations using fixed-point decimal arithmetic, as COBOL does, is that everyday numbers such as 0.1 can be represented exactly and the results of COBOL calculations do exactly match those that might be produced by hand.

Doing computations using floating-point arithmetic causes tiny inaccuracies that lead to unacceptable errors when taken over millions of computations. For instance, suppose you are required to calculate a tax of 15% on a 70-cent telephone call that is then rounded to the nearest cent2. Using the Java compiler at compileonline.com, the calculation 0.70 * 1.15 produces a result of 0.8049999999999999, which rounds down to 0.80 cents. The correct result should have been 0.805, which would round up to 0.81 cents. This difference of a cent per calculation, when taken over a million calculations of this kind, would result in an undercharge of ten thousand dollars.

If floating-point arithmetic is so problematic, how do languages that do not have native support for fixed-point decimal arithmetic deal with the problem? Nowadays, they implement a class to support decimal arithmetic operations. However, implementing such a class is not a trivial undertaking. Java’s original implementation of the BigDecimal class was so flawed that IBM raised a Java Specification Request (JSR)2 detailing the problems and requesting changes. These changes were implemented and shipped with Java 1.5 in 2004.

So does the revised BigDecimal class solve the problems with decimal arithmetic in Java? Only partly; decimal arithmetic in Java is implemented as a class instead of as a native type, and computations using the class are cumbersome, unnatural, and slow. For instance, Vorontsov1 found that 100 million BigDecimal calculations took 8.975 seconds, while the same number of double calculations took only 0.047 seconds. BigDecimal operations can be called unnatural in the sense that Java floating-point numbers and integers can use the standard assignment operator (=) and the standard arithmetic operators (+ - / *), whereas the BigDecimal class has to use its class methods. For instance, to multiply two BigDecimal numbers, you might use a statement like

calcResult = num1.multiply(num2);

instead of

calcResult = num1 * num2;

Early in Chapter 1, I showed you a Java program that used the BigDecimal class and asked you to compare it to the COBOL version for readability. I was confident then that you would be able to appreciate the readability of the COBOL version even though at the time you had not yet been introduced to the elements of the language. However, now that we have a more level playing field, let’s look at those programs again (reprinted Listings 1-1 and 1-2).

Listing 1-1. COBOL Version

IDENTIFICATION DIVISION.
PROGRAM-ID. SalesTax.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 beforeTax       PIC 999V99  VALUE 123.45.
01 salesTaxRate    PIC V999    VALUE .065.
01 afterTax        PIC 999.99.
PROCEDURE DIVISION.
Begin.
   COMPUTE afterTax ROUNDED = beforeTax + (beforeTax * salesTaxRate)
   DISPLAY "After tax amount is " afterTax.

Listing 1-2. Java Version (from http://caliberdt.com/tips/May03_Java_BigDecimal_Class.htm)

import java.math.BigDecimal;
public class SalesTaxWithBigDecimal
{
  public static void main(java.lang.String[] args)
  {
    BigDecimal beforeTax = BigDecimal.valueOf(12345, 2);
    BigDecimal salesTaxRate = BigDecimal.valueOf(65, 3);
    BigDecimal ratePlusOne = salesTaxRate.add(BigDecimal.valueOf(1));
    BigDecimal afterTax = beforeTax.multiply(ratePlusOne);
    afterTax = afterTax.setScale(2, BigDecimal.ROUND_HALF_UP);
    System.out.println( "After tax amount is " + afterTax);
  }
}

The COBOL version uses native fixed-point decimal arithmetic and is able to use all the standard arithmetic operators. The Java version has to use the BigDecimal methods to do even simple calculations. The COBOL program is 10 lines and 335 characters long, whereas the Java program is 13 lines and 484 characters long. Reminded me again. Which one is the verbose language?

Summary

This chapter explored the operation of advanced data-declaration clauses such as the REDEFINES clause, the RENAMES clause, and the USAGE clause. It showed how you can use REDEFINES clauses to redefine an area of storage with a new name and new data description. You saw how to use RENAMES to group a set of data items under a new name.  You also learned how to use the USAGE clause to change the default DISPLAY data format to one of the binary formats such as COMPUTATIONAL or PACKED-DECIMAL. The operation of these binary formats was explored in more depth, and the computational efficiency of the binary formats was weighed against the portability of the DISPLAY format. You investigated the operation-modifying SYNCHRONIZED clause and learned about the USAGE clause extensions provided by many COBOL implementers. The chapter ended with a discussion of the problems inherent in using floating-point arithmetic for financial and commercial calculations and the contrast between COBOL’s native support for decimal arithmetic and the bolted-on capability provided by Java’s BigDecimal class.

The next chapter returns to the topic of tabular data to introduce the SEARCH and SEARCH ALL verbs. Searching tabular data for a particular value is a common operation, but it can be tricky to get the search algorithms right. For this reason, COBOL provides SEARCH and SEARCH ALL. The SEARCH ALL verb allows you to apply a binary search to a table, and SEARCH applies a linear search.

LANGUAGE KNOWLEDGE EXERCISES

Sometimes the most instructive lessons arise from the mistakes you make. The debugging exercises that follow are based on some programming errors I made when I was learning to program in COBOL.

Locate your 2B pencil, and provide answers to the problems.

The Problems

The first two programs go into an infinite loop (never halt) and have to be stopped by the user. The third program sometimes crashes with the error message shown in the accompanying runs. The fourth program sometimes goes into an infinite loop.

Examine each program, and use the accompanying runs to discover the bug or bugs responsible for the problem. Identify the problem, and show how you would correct the program to make it work correctly.

Program 1

This program goes into an infinite loop. Examine the program in Listing 12-3 and the program output and try to figure out what is going wrong. Identify the problem, and suggest a solution.

Listing 12-3. Program Does Not Halt

IDENTIFICATION DIVISION.
PROGRAM-ID. Listing12-3.
AUTHOR.  Michael Coughlan.
 
DATA DIVISION.
WORKING-STORAGE SECTION.
01 Counters.
   02 Counter1        PIC 99.
   02 Counter2        PIC 99.
   02 Counter3        PIC 9.
 
PROCEDURE DIVISION.
Begin.
    DISPLAY "Debug 1.  Discover why I can't stop."
    PERFORM EternalLooping VARYING Counter1
        FROM 13 BY -5 UNTIL Counter1 LESS THAN 2
        AFTER Counter2 FROM 15 BY -4
              UNTIL Counter2 LESS THAN 1
        AFTER Counter3 FROM 1 BY 1
              UNTIL Counter3 GREATER THAN 5
 
    STOP RUN.
 
EternalLooping.
    DISPLAY "Counters 1, 2 and 3 are -> "
             Counter1 SPACE  Counter2 SPACE Counter3.
  
Answer:
______________________________________________________
______________________________________________________
______________________________________________________
______________________________________________________
______________________________________________________
______________________________________________________
______________________________________________________
______________________________________________________
______________________________________________________
______________________________________________________

9781430262534_unFig12-05.jpg

Program 2

This program also goes into an infinite loop. Examine the program in Listing 12-4 and the program output, and try to figure out what is going wrong. Identify the problem, and suggest a solution.

Listing 12-4. What Is Wrong with This Program?

IDENTIFICATION DIVISION.
PROGRAM-ID.  Listing12-4.
AUTHOR.  Michael Coughlan.
      
DATA DIVISION.
WORKING-STORAGE SECTION.
01    Counters.
    02    Counter1        PIC 99.
    02    Counter2        PIC 9.
    02    Counter3        PIC 9.
 
PROCEDURE DIVISION.
Begin.
   DISPLAY "Debug2.  Why can't I stop?"
   PERFORM EternalLooping VARYING Counter1
        FROM 1 BY 1 UNTIL Counter1 GREATER THAN 25
        AFTER Counter2 FROM 1 BY 1
                  UNTIL Counter2 GREATER THAN 9
        AFTER Counter3 FROM 1 BY 1
                  UNTIL Counter3 EQUAL TO 5
   STOP RUN.
 
 EternalLooping.
   DISPLAY "Counters 1, 2 and 3 are "
           Counter1 SPACE Counter2 SPACE Counter3.
Answer: _______________________________________
_______________________________________________
_______________________________________________
_______________________________________________
_______________________________________________________
_______________________________________________________

9781430262534_unFig12-06.jpg

Program 3

This program sometimes crashes. When it crashes, it produces the error message shown. From the two program outputs shown (one successful and one where the program crashes to produce the error message) and an examination of the program, try to work out why the program crashes. Identify the problem, and suggest a solution.

Listing 12-5. Program Crashes When Numbers Are Even; OK When Odd.

IDENTIFICATION DIVISION.
PROGRAM-ID. Debug3.
AUTHOR.  Michael Coughlan.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
    SELECT PersonFile ASSIGN TO "PERSON.DAT"
           ORGANIZATION IS LINE SEQUENTIAL.
            
DATA DIVISION.
FILE SECTION.
FD PersonFile.
01 PersonRec            PIC X(10).
   88 EndOfFile         VALUE HIGH-VALUES.
 
WORKING-STORAGE SECTION.
01 Surname              PIC X(10).
   88 EndOfData         VALUE SPACES.
01 Quotient             PIC 9(3).
01 Rem                  PIC 9(3).
01 NumberOfPeople       PIC 9(3) VALUE ZERO.
 
PROCEDURE DIVISION.
Begin.
   OPEN OUTPUT PersonFile
   DISPLAY "Debug3"
   DISPLAY "Enter list of Surnames."
   DISPLAY "Press RETURN after each name."
   DISPLAY "To finish press return with no value."
   DISPLAY "This will fill Surname with spaces"
   DISPLAY "Name -> " WITH NO ADVANCING
   ACCEPT Surname
   PERFORM GetPersons UNTIL EndOfData
   CLOSE PersonFile
 
   OPEN INPUT PersonFile
   READ PersonFile
       AT END SET EndOfFile TO TRUE
   END-READ
   PERFORM CountPersons UNTIL EndOfFile.
   CLOSE PersonFile
 
   DIVIDE NumberOfPeople BY 2
      GIVING Quotient REMAINDER Rem
 
   IF Rem = 0
       DISPLAY "Even number of people"
    ELSE
       DISPLAY "Odd number of people"
 
   STOP RUN.
 
GetPersons.
   WRITE PersonRec FROM Surname
   DISPLAY "Name -> " WITH NO ADVANCING
   ACCEPT Surname.
 
CountPersons.
   DISPLAY PersonRec
   ADD 1 TO NumberOfPeople
   READ PersonFile
      AT END SET EndOfFile TO TRUE
   END-READ.

9781430262534_unFig12-07.jpg

9781430262534_unFig12-08.jpg

Program 4

Sometimes this program goes into an infinite loop (does not halt). From the two program outputs shown (one where the program halts naturally and one where it has to be halted by the user) and an examination of the program, try to work out why the program sometimes does not halt. Identify the problem, and suggest a solution.

Listing 12-6. Program Sometimes Goes into an Infinite Loop

IDENTIFICATION DIVISION.
PROGRAM-ID. Debug4.
AUTHOR.  Michael Coughlan.
  
DATA DIVISION.
WORKING-STORAGE SECTION.
01 Counter1        PIC 99.
01 InNumber        PIC 9.
01 Result          PIC 999.
  
PROCEDURE DIVISION.
Begin.
    DISPLAY "DEBUG4.  Sometimes I just don't stop"
    DISPLAY "Enter number 0-9 :--> " WITH NO ADVANCING
    ACCEPT InNumber
    PERFORM EternalLooping
       VARYING Counter1 FROM 1 BY 1
       UNTIL Counter1 GREATER THAN 10
        
    DISPLAY "Back in main paragraph now"
    STOP RUN.
  
EternalLooping.
    COMPUTE Result = InNumber * Counter1
    IF Result > 60
        MOVE 99 TO Counter1
    END-IF
    DISPLAY "Counter1 = " Counter1 "  Result = " Result.

9781430262534_unFig12-09.jpg
9781430262534_unFig12-10.jpg

LANGUAGE KNOWLEDGE EXERCISES—ANSWERS

Program 1

Problem Cause

The problem here is that Counter1 and Counter2 go negative but are described as PIC 99a description that only allows positive values. The problem with Counter1 is masked by the problem with Counter2. You can see the effect of the problem with Counter2 in the program output fragment shown in Figure 12-16. When Counter2 has a value of 03, the next value it should take is -1; but because Counter2 is described as PIC 99, it cannot hold a negative value. This means the sign is lost, and instead of -1, the value of Counter2 is 1. Therefore, Counter2 never reaches its terminating value, and the loop never terminates.

PERFORM EternalLooping VARYING Counter1
        FROM 13 BY -5 UNTIL Counter1 LESS THAN 2
        AFTER Counter2 FROM 15 BY -4
              UNTIL Counter2 LESS THAN 1
        AFTER Counter3 FROM 1 BY 1
              UNTIL Counter3 GREATER THAN 5

9781430262534_Fig12-16.jpg

Figure 12-16. Fragment of output from Listing 12-3 highlighting the problem area

Problem Solution

The solution to the problem is to describe Counter1 and Counter2 as PIC S99.

Program 2

Problem Cause

The problem here is that Counter2 is described as PIC 9. You can see the problem by examining the flowchart in Figure 12-17.

9781430262534_Fig12-17.jpg

Figure 12-17. Flowchart showing how the three-counter PERFORM..VARYING works

Suppose the program is at the point where Counter2 has a value of 9 and Counter3 has a value of 5. At this point the condition Counter3 = 5 is satisfied, and Counter3 is reset to 1 while Counter2 is incremented, making it equal to 10. Because Counter2 is described as PIC 9, there is only room for one digit, so the 1 is truncated, leaving Counter2 with a value of 0. When the Counter2 > 9 condition is tested, it is not satisfied, and the loop never ends.

Problem Solution

The solution to the problem is to describe Counter2 as PIC 99.

Program 3

Problem Cause

The problem here is that the IF before the STOP RUN does not have an explicit terminator. This means the scope of the IF is terminated by the period that follows the STOP RUN; and this means the scope of the ELSE branch of the IF includes the STOP RUN:

   DIVIDE NumberOfPeople BY 2
      GIVING Quotient REMAINDER Rem
 
   IF Rem = 0
       DISPLAY "Even number of people"
    ELSE
       DISPLAY "Odd number of people"
 
   STOP RUN.
 
GetPersons.
   WRITE PersonRec FROM Surname
   DISPLAY "Name -> " WITH NO ADVANCING
   ACCEPT Surname.

Failing to specify the scope of the IF with an explicit terminator has the following effect. When there is an odd number of people, the ELSE branch is taken, the STOP RUN is executed, and the program stops normally; but when there is an even number of people, the ELSE branch is not taken, the STOP RUN is not executed, and control falls into the GetPersons paragraph where it tries to write to the closed PersonFile. This write attempt crashes the program and produces the error message.

Problem Solution

Add an explicit scope delimiter to the IF statement:

   IF Rem = 0
       DISPLAY "Even number of people"
    ELSE
       DISPLAY "Odd number of people"
   END-IF
 
   STOP RUN.

Program 4

Problem Cause

The problem with this program is that the programmer tries to modify the PERFORM..VARYING counter variable Counter1 in order to force the loop to terminate prematurely. Unfortunately, the programmer has not consulted the flowchart shown in Figure 12-18. That flowchart shows that as control exits, the paragraph Counter1 is incremented. Because Counter1 has been given a value of 99 in the paragraph, the increment brings it to 100. But Counter1 is described as PIC 99 and only has room for two digits. This means the 1 is truncated, leaving Counter1 with a value of 00. Because the terminating condition Counter1 > 10 has not been satisfied, the loop will not terminate.

9781430262534_Fig12-18.jpg

Figure 12-18. Flowchart showing how the single-counter PERFORM..VARYING works

Problem Solution

A simple solution is to change the description of Counter1 to PIC 999. A more complex solution would require you rewrite the program so that it does not require the counting variable to be changed in the loop. When you use a construct such as the PERFORM..VARYING, you make a contract with the reader that construct will operate in the normal way and that the counting variable will take the values specified in the PERFORM. If you break that contract, you create uncertainty in the mind of the reader not only for this loop but also for all the loops in the program. Now the reader has to scrutinize each one to make sure they work as expected.

References

  1.  Vorontsov M. Using double/long vs. BigDecimal for monetary calculations. 2013 Feb. http://java-performance.info/bigdecimal-vs-double-in-financial-calculations/

  2.  IBM. Decimal arithmetic FAQ. 2008. http://speleotrove.com/decimal/decifaq.html

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.173.249