COBOL COMPUTE calculation

会有一股神秘感。 提交于 2019-12-22 18:49:14

问题


I am executing a standalone Enterprise COBOL program with the below calculation. I am having a COMPUTE with multiple operations and another with the full calculation split up. But the results are different(the last 4 digits) in both cases.

I have manually calculated these using a calculator and the result matches the one with the split up COMPUTE statements. I have tried the calculations by using the entire answer at intermediate results and use only 15 digits of the final answer and also using only 15 digits at all intermediate steps(without rounding). But none of these results match with the combined COMPUTE result.

Could someone help me understand why there is such a difference.

      05 WS-A              PIC S9(3)V9(15) COMP.
      05 WS-B              PIC S9(3)V9(15) COMP.
      05 WS-C              PIC S9(3)V9(15) COMP.
      05 WS-D              PIC S9(3)V9(15) COMP.
      05 WS-E              PIC S9(3)V9(15) COMP.
      05 WS-RES            PIC S9(3)V9(15) COMP.  
      05 RES-DISP          PIC -9(2).9(16).

       MOVE 3.56784                TO WS-A.
       MOVE 1.3243284234           TO WS-B.
       MOVE .231433897121334834    TO WS-C.
       MOVE 9.3243243213           TO WS-D.
       MOVE 7.0                    TO WS-E.   

       COMPUTE WS-RES = WS-A / WS-B.
       MOVE WS-RES TO RES-DISP.
       COMPUTE WS-RES = WS-RES / WS-C.
       MOVE WS-RES TO RES-DISP.   
       COMPUTE WS-RES = WS-RES / (WS-D + WS-E).
       MOVE WS-RES TO RES-DISP.   
       COMPUTE WS-RES = WS-RES * (WS-C - WS-A) 
       MOVE WS-RES TO RES-DISP.   
       COMPUTE WS-RES = WS-RES + WS-E ** WS-B 
       MOVE WS-RES TO RES-DISP.   
       COMPUTE WS-RES = WS-RES + WS-D.
       MOVE WS-RES TO RES-DISP.   

Result of last compute = 20.1030727225138740

       COMPUTE WS-RES = WS-A / WS-B / WS-C /
               (WS-D + WS-E) * (WS-C - WS-A) +
               WS-E ** WS-B + WS-D.
       MOVE WS-RES TO RES-DISP.   

Result of combined compute = 20.1030727225138680


回答1:


Slow, I know, but I think I've just got to why you've defined everything with 15 decimal places. You couldn't get it to work otherwise.

Read the question in the link lower down (and the answer, of course). You do not need to specify all fields with the precision you require for the output.

Re-arrange your COMPUTE. Exponentiation outside the main COMPUTE. Multiply first. Then divide. Any additions/subtractions fit in naturally. Use parenthesis to exactly specify how you want a human to read the COMPUTE (the compiler doesn't care, it'll do what it is told, but at times people don't know what they are telling it).

If you do this (correctly) you will get the same answer as in your COMPUTE with all fields having 15 decimal places.

If you don't do this, your COMPUTE (and others when you copy it) will always be fragile and prone to error when changed.

It was a good idea to break out the COMPUTE into smaller ones like you did so that you could see which values to put into your calculator. You can do the same thing when you make the fields their correct sizes.

I'm going to have to totally re-write this, as the multiple updates are making it messy... at some point.

OK, confirmed. The difference is due to the calculation of a non-integer exponentiation within the COMPUTE which, as the manual says, then converts everything in the COMPUTE (all the intermediate fields) into floating-point numbers, which have a higher number of decimal places than the 15 specified in the PICture clauses.

There is now a diagnostic message (having taken the exponentiation out) due to the multiplication, which would like to have 36 digits, but can only have 30 (ARITH(COMPAT)) or 31 (ARITH(EXTEND)). If high-order data is truncated through this, there will be a run-time message.

Note. With ARITH(COMPAT) 15 is the largest number of significant digits where precision will not be lost (64-bit floating-point). ARITH(EXTEND) guarantees precision, but there is an overhead in processing (128-bit floating-point).

Back to earlier...

Been thinking more about this. You are using 18 digits, and you haven't mentioned using ARITH(EXTEND) as a compile option and haven't mentioned any diagnostic messages being produced for the large COMPUTE. Which is interesting.

Haven't done much exponentiation in COBOL, and then only with whole numbers. So I looked at the manual. Because of the fractional exponentiation everything in your large COMPUTE is being done in floating-point. That doesn't matter as such, but it means things are being done with greater precision than is expected from the 15 decimals in your definition. In your small COMPUTEs, this is not happening.

I'd suggest taking the exponentiation out of the big COMPUTE, calculating that separately and simply putting that result in the big COMPUTE (a simple addition replacing the exponentiation). I suspect at that stage the compiler will start to moan about the number of significant digits in results. If it does, then you will get a run-time message if you actually lose a significant digit.

You should:

  1. Take the exponentiation out of the big COMPUTE and replace it with the result of a separate COMPUTE of the exponentiation
  2. Define each field to its maximum size required by the data (not the maximum possible for everything)
  3. (Probably) change to COMP-3 from COMP, but test it yourself
  4. Parenthesise everything so that the human reader knows the order the compiler will do things in
  5. If you still have warnings about possible truncation in the COMPUTE, look at ARITH(EXTEND) compiler option, but don't just put it in as a fix, only use it if you need it, and document its use for that program

I will try to confirm this later, but I think that will sort it out.

The following was the start, and still applies in general, although not directly relevant for the specific question (the problem being the higher floating-point precision forced onto everything vs only forced for the exponentiation):

Your problem with the small COMPUTEs is that you are not executing them in the same order as the elements of the large COMPUTE.

The ( and ) are not there for fun, or just to group things together, they establish precedence in the calculations.

What else establishes precedence? The operator used. What is the order of precedence? Well, you have to look that up in the manual, memorise it, or familiarise yourself with it each time if you forget. Mmmmm.... not a good suggestion.

Plus, other people will be working on the programs that you write or change. And they may "know" how a COMPUTE works (by which I mean they don't, but think they do, so won't look it up). Doubly-not-good suggestion.

So....

Use ( and ) and define the order in which you want things done.

Also be aware of where you may lose significance. Have a look at this one, AS/400: Using COMPUTE function, inconsistent results with different field definition, read up and understand the referenced parts of the Enterprise COBOL manuals.

As a summary of the linked-to question on this site, multiply first and divide last, to ensure that intermediate results do not lose significant digits. Unless you deliberately want to lose digits, in which case do those COMPUTEs to lose significance individually, and comment the code, so that no-one "fixes" it.

Also it is unusual on the Mainframe to use COMP/COMP-4/BINARY/COMP-5 for fields with decimal places. Once you are happy with your COMPUTEs, copy the program and change the field definitions to COMP-3/PACKED-DECIMAL. Put a loop on a counter in each program, and see if you notice any significant difference in CPU usage.




回答2:


I copied, compiled and ran your program with a couple of minor changes:

  • Declared RES-DISP as PIC -9(3).9(15) to avoid compiler truncation warnings
  • Added new variable WS-EXP PIC S9(3)V9(15) COMP
  • Added a new computation doing as Bill Woodger suggested by breaking the exponentiation into a seperate calculation as follows:
  COMPUTE WS-EXP = WS-E ** WS-B 
  COMPUTE WS-RES = WS-A / WS-B / WS-C /  
     (WS-D + WS-E) * (WS-C - WS-A) +  
     WS-EXP + WS-D.

The only compiler warning issued in this program was for the second COMPUTE statement above. The message was:

IGYPG3113-W Truncation of high-order digit positions may occur due to intermediate results exceeding 30 digits

The result of the COMPUTE above is exactly the same as your fragmented calculation: 020.103072722513874. Your all-in-one COMPUTE statement did not cause a compiler warning. But the internal exponentiation caused higher precision intermediate results to be used throughout the calculation (fewer roundings) yielding a slightly different result: 020.103072722513868.

Another interesting observation here. Using ARITH(COMPAT) I get exactly the same results as peresented in the question, using ARITH(EXTEND) I get 020.103072722513877 for the fragmentary calculation and 020.103072722513874 for the all-in-one COMPUTE (which is the same as the fragmentary calculation when compiling with ARITH(COMPAT)).

All goes to show that you need to really study numeric precision, rounding and truncation rules when doing complex computations. This is especially true of COBOL because of the number of different numeric data types available to the programmer.



来源:https://stackoverflow.com/questions/22679589/cobol-compute-calculation

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!