Reputation: 100
I have a compute statement that uses fields like so:
WS-COMPUTE PIC 9(14).
WS-NUM-1 PIC 9(09).
WS-NUM-2 PIC 9(09).
WS-NUM-3 PIC S9(11) COMP-3.
WS-DENOM PIC 9(09).
And then there is logic to make a computation
COMPUTE WS-COMPUTE =
((WS-NUM-1 - WS-NUM-2 + WS-NUM-3)
/ WS-DENOM) * 100
The * 100 is in there because a number < 1 is expected from the division, but 0 is what was always stored in WS-COMPUTE.
We got a workaround by declaring another field that did have implied decimals, and then moving that to value to WS-COMPUTE, but I was lost on why the original would always populate WS-COMPUTE with 0?
Upvotes: 1
Views: 4189
Reputation: 13076
The number of decimal places for the results of intermediate calculations are directly related to the number of decimal places in your the final result field (you can consult the manual in the case where you have multiple result fields) when there are no decimal places in the individual operands. COBOL is not going to use a predetermined number of decimal places for intermediate results. If neither actual operands in question nor final result contain decimal places, the intermediate result will not contain decimal places.
The relationship is: number of decimal places in intermediate results = number of decimal places in final result field. The only thing which can modify this is the specification of ROUNDED. If ROUNDED is specified, one extra decimal place is kept for the intermediate result fields, and that will be used to perform the rounding of the final result.
You have no decimal places on your final result, and no ROUNDED. So the intermediate results will have no decimal places. If you get a value of less than zero, then it is gone before anything can happen to it. It is stored as zero, because there is no decimal part available to store it in.
You need to understand COMPUTE before you use it. Nowhere near enough people do. There is absolutely no need to specify excessive lengths of fields or decimal places where none are needed. These a common ways to "deal with" a problem, but are unnecessary, as the actual problem is a poorly-formed COMPUTE.
If your COMPUTE contains multiplication, do that first. If it contains division, do that last. This may require re-arranging a formula, but this will give you the correct result. Subject to truncation, which comes in two parts, as Bruce Martin has indicated. There is the one you are getting, decimal truncation through not specifying enough (any) decimal places when you expect a decimal-only value for an intermediate result, and high-order truncation if your source fields are not big enough. Always remember that the result field controls the size (decimal and integer) of the intermediate results. If you do those things, your COMPUTEs will always work.
And consider whether you want the final result rounded. If so, use ROUNDED. If you want intermediate results to be rounded, you need to do that yourself with separate COMPUTEs or DIVIDEs or MULTIPLYs.
If you don't take these things into account, your COMPUTEs will work by accident, or sometimes, or not at all, or when you specify excessive size or decimal places. Always remember that the result field controls the size (decimal) of the intermediate results where operands contain no decimal places.
If you don't need any decimal places in the final result, use Bruce Martin's first COMPUTE:
COMPUTE WS-COMPUTE = (((WS-NUM-1 - WS-NUM-2 + WS-NUM-3) * 100) / WS-DENOM
If you do need decimal places, use Bruce Martin's first COMPUTE (yes, the same one) with the decimals defined on the final result (WS-COMPUTE).
If you need the result to be rounded (0-4 down, 5-9 up) use ROUNDED. If you need some other rounding, specify the final result with an extra decimal place beyond what you need, and do your own rounding to your specification.
If you look at the column to the right of your question, under Related
, you'll find existing questions here which would/should have answered this one for you.
You do not need to add spurious digits or spurious decimal places to everything in sight. Ensure your final result is big enough, has enough decimal places, and pay attention to the order of things. Read your manual which should document intermediate results. If your manual does not cover this, the IBM Enterprise COBOL manuals are an excellent general reference, as well as specific ones. The Programming Guide devotes an entire Appendix to intermediate results.
Upvotes: 1
Reputation: 10553
The default for Cobol is normally to truncate !!. This includes intermediate results. So the decimal places will be truncated in your calculation
You could try:
COMPUTE WS-COMPUTE = (((WS-NUM-1 - WS-NUM-2 + WS-NUM-3) * 100) / WS-DENOM
This could result in loosing top order digits. Alternatively you could
03 WS-Temp Pic 9(11)V9999 comp-3.
Compute WS-Temp = WS-NUM-1 - WS-NUM-2 + WS-NUM-3.
Compute WS-Temp = (WS-Temp / WS-DENOM) * 100.
Compute WS-COMPUTE = WS-Temp.
Change the field definition:
WS-COMPUTE PIC 9(14).
WS-NUM-1 PIC 9(09)V999.
WS-NUM-2 PIC 9(09)V999.
WS-NUM-3 PIC S9(11)V999 COMP-3.
WS-DENOM PIC 9(09).
Upvotes: 0
Reputation: 4263
It sounds like you are using the TRUNC(STD) option, the compiler takes the picture clause to decide what precision to use for intermediate results. You can either add implied decimals to all your intermediate fields or try something like TRUNC(BIN) or TRUNC(OPT), though in this case, I don't think they will help.
Truncates final intermediate results. OS/VS COBOL has the TRUNC and NOTRUNC options (NOTRUNC is the default). VS COBOL II , IBM COBOL, and Enterprise COBOL have the TRUNC(STD|OPT|BIN) option. TRUNC(STD) Truncates numeric fields according to PICTURE specification of the binary receiving field TRUNC(OPT) Truncates numeric fields in the most optimal way TRUNC(BIN) Truncates binary fields based on the storage they occupy TRUNC(STD) is the default.
For a complete description, see the Enterprise COBOL Programming Guide.
Upvotes: 1