Michael Coughlan

Home > Other > Michael Coughlan > Page 37


  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 292

  Chapter 12 ■ advanCed data deClaration

  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));

  293

  Chapter 12 ■ advanCed data deClaration

  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.

  294

  Chapter 12 ■ advanCed data deClaration

  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:

  ______________________________________________________

  ______________________________________________________

  ______________________________________________________

  ______________________________________________________

  ______________________________________________________

  ______________________________________________________

  ______________________________________________________

  ______________________________________________________

  ______________________________________________________

  ______________________________________________________

  295

  Chapter 12 ■ advanCed data deClaration

  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: _______________________________________

  _______________________________________________

  _______________________________________________

  _______________________________________________

  _______________________________________________________

  _______________________________________________________

  296

  Chapter 12 ■ advanCed data deClaration

  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

  297

  Chapter 12 ■ advanCed data deClaration

  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.

  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.

  298

  Chapter 12 ■ advanCed data deClaration

  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.

  LaNGUaGe KNOWLeDGe eXerCISeS—aNSWerS

  Program 1

  Problem Cause

  The problem here is that Counter1 and Counter2 go negative but are described as PIC 99—a 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

  299

  Chapter 12 ■ advanCed data deClaration

/>   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.

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

  300

  Chapter 12 ■ advanCed data deClaration

  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.

 

‹ Prev