The Answer: MVS COBOL & rounding
Author Message The Answer: MVS COBOL & rounding

To review:
field-a pic 9(3) value 0.
field-b pic 9(7) value 2600.
field-c pic 9(7) value 3094

compute  field-a  rounded  =  (field-b  /  field-c)  *  100

The result should be 84, but the above code gives a value to field-a of 80.

Because all fields are integers, the fractional part of the result is
dropped.  With the "rounded" option, however, the tenths position is saved
so that the program has a basis to round from; something like this:

field-a  =  (2600  /  3094)  *  100
=  (.8403361)  *  100
=  (.8)  *  100
=  80

Without the "rounded" option, the following would occur.

field-a  =  (2600  /  3094)  *  100
=  (.8403361)  *  100
=  (0)  *  100
=  0

The solution is to define field-b as pic 9(7)V99.

P.

Sun, 08 Oct 2000 03:00:00 GMT  The Answer: MVS COBOL & rounding

No, no, no!!!

While your answer works for this **specific** case, as soon as any of
the pieces changes (say, field-a changes definition to 9(3)v9), your
solution STOPS WORKING.  Again, you have incorrect results.

The only reliable solution, that works no matter how the numbers are
defined, that works no matter what the values of the numbers are, and
that even works for other languages in addition to COBOL, is to
rearrange the calculation so that no division is done until the last
step.

In this case, that means changing the compute statement to read:

compute field-a rounded = (field-b * 100) / field-c

This works.

Quote:

> To review:
>         field-a pic 9(3) value 0.
>         field-b pic 9(7) value 2600.
>         field-c pic 9(7) value 3094

>         compute  field-a  rounded  =  (field-b  /  field-c)  *  100

> The result should be 84, but the above code gives a value to field-a of 80.

> Because all fields are integers, the fractional part of the result is
> dropped.  With the "rounded" option, however, the tenths position is saved
> so that the program has a basis to round from; something like this:

> field-a  =  (2600  /  3094)  *  100
>             =  (.8403361)  *  100
>             =  (.8)  *  100
>             =  80

> Without the "rounded" option, the following would occur.

> field-a  =  (2600  /  3094)  *  100
>             =  (.8403361)  *  100
>             =  (0)  *  100
>             =  0

> The solution is to define field-b as pic 9(7)V99.

> P.

--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
They're just my opinions...
Clifton Ivy, Mgmt Info, Purdue University

Sun, 08 Oct 2000 03:00:00 GMT  The Answer: MVS COBOL & rounding

"No, no, no!!!"

You method only works if you HAPPEN to have a factor of 100 involved
somewhere in the calculation.  The way to handle this in COBOL is to
UNDERSTAND the intermediate result rules for the compiler that you are
using.  There is *always* a way to gee the results that you want - if you
understand the rules (which vary from compiler to compiler) and define your
fields correctly for the calculation you want.

In fact, the current ANSI Standard allows the compiler to do the calculation
(almost) anyway it wants.  This means that it is ANSI conforming to have

compute field-x = 9

put a value of 2 in field-x on Mondays and a value of 3 on other weekdays.
(I don't think any compiler would do this - but it is allowed by the
Standard).  Therefore, your suggestion requires knowledge of the compilers
rules - just as much as any other suggestion.

FYI - the draft of the next COBOL Standard *does* provide an option for
"ARITHMETIC IS STANDARD" which would guarantee the correct results with the
original code - and yours - and any other "logically" equivalent code.
No, no, no!!!

--
+ +
+   Bill Klein -
"C" is a nice letter to START the name of your programming language
with
but I wouldn't want to end up there.

Quote:

>No, no, no!!!

>While your answer works for this **specific** case, as soon as any of
>the pieces changes (say, field-a changes definition to 9(3)v9), your
>solution STOPS WORKING.  Again, you have incorrect results.

>The only reliable solution, that works no matter how the numbers are
>defined, that works no matter what the values of the numbers are, and
>that even works for other languages in addition to COBOL, is to
>rearrange the calculation so that no division is done until the last
>step.

>In this case, that means changing the compute statement to read:

>  compute field-a rounded = (field-b * 100) / field-c

>This works.

>> To review:
>>         field-a pic 9(3) value 0.
>>         field-b pic 9(7) value 2600.
>>         field-c pic 9(7) value 3094

>>         compute  field-a  rounded  =  (field-b  /  field-c)  *  100

>> The result should be 84, but the above code gives a value to field-a of
80.

>> Because all fields are integers, the fractional part of the result is
>> dropped.  With the "rounded" option, however, the tenths position is
saved
>> so that the program has a basis to round from; something like this:

>> field-a  =  (2600  /  3094)  *  100
>>             =  (.8403361)  *  100
>>             =  (.8)  *  100
>>             =  80

>> Without the "rounded" option, the following would occur.

>> field-a  =  (2600  /  3094)  *  100
>>             =  (.8403361)  *  100
>>             =  (0)  *  100
>>             =  0

>> The solution is to define field-b as pic 9(7)V99.

>> P.

>--
>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>They're just my opinions...
>Clifton Ivy, Mgmt Info, Purdue University

Sun, 08 Oct 2000 03:00:00 GMT  The Answer: MVS COBOL & rounding

somewhere in the calculation.  The way to handle
this in COBOL is to
UNDERSTAND the intermediate result rules for the
compiler that you are
using.  There is *always* a way to gee the results
that you want - if you
understand the rules (which vary from compiler to
compiler) and define
fields correctly for the calculation you want.

Holy shit!

Write code, deliberately, that fails when run on a
different compiler?  Deliberately
design the system that way?  So that it fails in a
nice invisible way if you try
to convert it?  If that is the RIGHT way to work,
I'd hate to see what you consider
poor coding practice.

The correct way to do it is to never have any
deliberately undefined intermediate
answers.  If that means you have to avoid the use
of the compute, or split it into
two computes with an extra parameter, so be it.
Write the damned code so that it
works forever.

Mon, 09 Oct 2000 03:00:00 GMT  The Answer: MVS COBOL & rounding

The problem is that the current Standard provides *no* way to avoid entirely implementor defined situations.  It is not just "intermediate results" that are implementor defined.  It is *all* arithmetic expressions that are implementor defined.

As I said earlier, this means that
Compute any-field = 1
is implementor defined.

It also means that
Move XYZ (1:) to ABC
is implementor defined (because the "1" is an arithmetic expression

Furthermore,
If A = 2
is (probably) implementor defined (because the format means that "2" can be viewed as an arithmetic expression.

This is a PROBLEM with the current COBOL Standard - which is "solved" in the next Standard.  But if you don't understand this, there is no way that you can understand how your current COBOL program will work - or when it *MIGHT* give different results when you port it to another implementation.

--
+ +
+   Bill Klein -
"C" is a nice letter to START the name of your programming language with
but I wouldn't want to end up there.

somewhere in the calculation.  The way to handle this in COBOL is to
UNDERSTAND the intermediate result rules for the compiler that you are
using.  There is *always* a way to gee the results that you want - if you
understand the rules (which vary from compiler to compiler) and define
fields correctly for the calculation you want.

Holy shit!

Write code, deliberately, that fails when run on a different compiler?  Deliberately
design the system that way?  So that it fails in a nice invisible way if you try
to convert it?  If that is the RIGHT way to work, I'd hate to see what you consider
poor coding practice.

The correct way to do it is to never have any deliberately undefined intermediate
answers.  If that means you have to avoid the use of the compute, or split it into
two computes with an extra parameter, so be it.  Write the damned code so that it
works forever.

Mon, 09 Oct 2000 03:00:00 GMT  The Answer: MVS COBOL & rounding

No, I must not have done a good job explaining.

The only reason there was a factor of 100 in the calculation is that it
was there from the beginning.  See the original calculation (way) below.

The only knowledge of the methods of the compiler you need to know is
how to effect the sequence of calculations.  This is typically by
surrounding parts of the calculation by parentheses, as with COBOL.

Getting back to the "100", it doesn't matter what the values of the
numbers are.  Any time you do division, you end up with a rounding (or
truncating) "error" because you have to stop calculating some time.  If
you then multiply that result by a number, the rounding effect is also
multiplied, by the same number.  In the example that started this whole
thread, the number was 100.  That made the rounding effect 100 times as
large as it was in the intermediate result, and that made it effect the
final result.

If, on the other hand, you do all your multiplying first, and then the
division, you don't fall into this problem.

Multiplying, in contrast to division, results in a fixed set of decimal
places in the result, the sum of decimal places in both numbers.  (If
you multiply a number with 2 decimal places by a number with 3 decimal
places, the result with have 5 decimal places; possibly not all
significant, depending on the numbers.)

Even if you round or truncate the result of a multiplication to fewer
places than the natural result, when you divide this result, the
rounding effect is also divided.

Let's try an example.  But first, I feel that the "100" and "2600" in
the initial example can obscure rounding oddities, since so many zeros
show up.  Lets pick some close but different numbers, say 127 and 2675
(or try it with any numbers).

If you do the divide first:

2675 / 3094 = 0.8645765 (by my \$3 calculator)

127 * 0.8645765 = 109.80121   pretty good; rounds to 110 correctly
127 * 0.864576  = 109.80115   still pretty good
127 * 0.86457   = 109.80039   still OK
127 * 0.8645    = 109.7915    starting to slip
127 * 0.864     = 109.728     slipping further
127 * 0.86      = 109.22      rounds to 109; wrong result
127 * 0.8       = 101.6       the units digit is way off

This shows that, with these numbers and wanting a whole-number rounded
result, if the intermediate result is not carried to at least 3 decimal
places (0.864) you get the wrong result.

You can also easily see that if the multiplier (127) were larger (say
12,700), you would need more decimal places to not affect the rounded
whole number result -- 5 in this case.  I see now I should have rounded
each decimal result above; if I had, I would have only needed 4 decimal
places to get the correct 12,700 * 0.8646 = 10,980.42, which rounds to
10,980.

If you do the multiply first:

2675 * 127 = 339,725

Since there were no decimal places in the numbers to be multiplied,
there are no decimal places in the result.  But for grins, let's see how
may digits to the *left* of the decimal point we would have to lose to
affect the result.

339,725 / 3094 = 109.80122  which rounds to 110, the correct result
339,720 / 3094 = 109.79961  OK
339,700 / 3094 = 109.79314  still OK
339,000 / 3094 = 109.56690  questionable, depends on rounding method
330,000 / 3094 = 106.65804  which is wrong

If I had rounded the 4th line above to 340,000 / 3094, I would have
gotten 109.8901, which would have rounded to the correct 110, removing
the "questionable" comment.

If the multiplier had been the larger 12,700 as in the second divide
example, it still doesn't affect the result.  I can lose up to 3 digits
to the *left* of the decimal place and not affect the final result.

So, when the multiply is done first, assumptions the compiler makes
about how many decimal places to use for an intermediate result don't
have the same effect as when the divide is done first.  As long as the
multiply gives the correct result, the final result will be correct to
however many decimal places it is carried to.

That was the point I was trying to make.

Sorry for being so long-winded!

Quote:

>    "No, no, no!!!"

> You method only works if you HAPPEN to have a factor of 100 involved
> somewhere in the calculation.  The way to handle this in COBOL is to

> >No, no, no!!!
snip...
> >The only reliable solution, that works no matter how the numbers are
> >defined, that works no matter what the values of the numbers are, and
> >that even works for other languages in addition to COBOL, is to
> >rearrange the calculation so that no division is done until the last
> >step.

> >In this case, that means changing the compute statement to read:

> >  compute field-a rounded = (field-b * 100) / field-c

snip...

The original calculation:

- Show quoted text -

Quote:
> >>         compute  field-a  rounded  =  (field-b  /  field-c)  *  100

Tue, 10 Oct 2000 03:00:00 GMT  The Answer: MVS COBOL & rounding

Quote:

>To review:
>        field-a pic 9(3) value 0.
>        field-b pic 9(7) value 2600.
>        field-c pic 9(7) value 3094

>        compute  field-a  rounded  =  (field-b  /  field-c)  *  100

>The result should be 84, but the above code gives a value to field-a of 80.

>Because all fields are integers, the fractional part of the result is
>dropped.  With the "rounded" option, however, the tenths position is saved
>so that the program has a basis to round from; something like this:

>field-a  =  (2600  /  3094)  *  100
>            =  (.8403361)  *  100
>            =  (.8)  *  100
>            =  80

>Without the "rounded" option, the following would occur.

>field-a  =  (2600  /  3094)  *  100
>            =  (.8403361)  *  100
>            =  (0)  *  100
>            =  0

>The solution is to define field-b as pic 9(7)V99.

>P.

I do not agree what was written before. In the example a basic error was made:
never multiply after dividing.
The coorect code should haven been:
field-a = (2600 * 100) / 3094
Here rounding is done once at the end of the calculation.
Many people think that multiplying is done before dividing, but in the real
world multiplying and dividing are equal in priority so you have to force the
wanted order.

Johan

Fri, 13 Oct 2000 03:00:00 GMT

 Page 1 of 1 [ 7 post ]

Relevant Pages