10

I have this very simple dummy COBOL program which does a dummy COMPUTE and displays the result.

   ID DIVISION.
   PROGRAM-ID. DUMMYPGM.
   DATA DIVISION.
   WORKING-STORAGE SECTION.
   01 NUM-A PIC 9(3) VALUE 399.
   01 NUM-B PIC 9(3) VALUE 211.
   01 NUM-C PIC 9(3).
  *
   PROCEDURE DIVISION.
   MAIN.
       COMPUTE NUM-C = ((NUM-A / 100) - (NUM-B / 100)) * 100
       DISPLAY 'NUM-C IS ' NUM-C
       STOP RUN.

When I compile this code on a mainframe (with compiler MVS Enterprise COBOL V4.2) and execute it, I get "NUM-C IS 100", probably because (399 / 100) is treated as a 3 instead of a 3.99 in the calculation (and same goes for 211 / 100).

But when I compile the same exact code on a PC (with the GnuCobol compiler) and execute it, I get "NUM-C IS 188". The PC's answer is correct , but I would like to make it behave like the mainframe (and thus, losing precision in that compute statement to give 100 instead of 188)... How would I do that ?

The reason for the above is a general expression of this code:

 COMPUTE PDISCR = (((((X(1) + DX - XBRAK) * (ABRAK(1) / 1000)) / 100)
                  + PHT(1) + DPH - PHBRAK) * 2) + ((V(1) + DV 
                  + VBRAKMPM) * (V(1) + DV - VBRAKMPM) / 100000)) 

This is part of a 50-year-old Train simulation program, which I need to migrate to GnuCOBOL. All the fields used in the COMPUTE are integers. I need to be able to get the same answer from GnuCOBOL.

Confirmed for OpenCOBOL/GnuCOBOL up to 2.0.

Bill Woodger
  • 12,702
  • 4
  • 35
  • 43
Gael L
  • 223
  • 1
  • 11
  • Sorry for the misunderstanding : by complex, I meant several operations in a single compute (here, there are 2 divisions and one multiplication instead of one operation). I recognize that the code is not very good, but it was just a proof of concept. I did at all want to sound superior or anything, so I have edited my post. – Gael L Oct 04 '16 at 16:46
  • As for the GnuCOBOL release, I wrote cobc -V and got this : cobc (OpenCOBOL) 1.1.0 Copyright (C) 2001-2009 Keisuke Nishida / Roger While Built Sep 10 2010 10:12:33 Packaged Feb 06 2009 10:30:55 CET – Gael L Oct 04 '16 at 16:46
  • I know that a possible solution would be to isolate every step of the calculation in different integer variables, but I want to find a better solution, because here is an example of COMPUTE statement from the code I am working on : COMPUTE PDISCR = (((((X(1) + DX - XBRAK) * (ABRAK(1) / 1000)) / 100) + PHT(1) + DPH - PHBRAK) * 2) + ((V(1) + DV + VBRAKMPM) * (V(1) + DV - VBRAKMPM) / 100000)) – Gael L Oct 04 '16 at 16:49
  • imo, Hmm, as you insist on using integers everywhere then do all the multiplications first then do the divisions? imo, You need to read about how `compute` processes intermediate results. Note, I meed to read the details as well. – Ryan Vincent Oct 04 '16 at 16:51
  • Yes, I shall read about that. I know that GnuCobol transmits Cobol into C before compiling it, so maybe it then processes the resulting intermediate calculations differently. – Gael L Oct 04 '16 at 16:55
  • The correct COMPUTE is `COMPUTE NUM-C = ( NUM-A - NUM-B ) * 100 / 100` as @RyanVincent has mentioned. This will get you 188 with Enterprise COBOL. It does look as though GnuCOBOL uses different intermediates (err... floating-point?), and it seems they may have changed in the 2002-now-2014 Standard. Looking further. – Bill Woodger Oct 04 '16 at 17:00
  • It does mean that a "bad" COMPUTE from the Mainframe is going to get a different result with GnuCOBOL. – Bill Woodger Oct 04 '16 at 17:00
  • Okay. From another Stack Overflow thread's answer, I learned that "The relationship is: number of decimal places in intermediate results = number of decimal places in final result field." This sentence is indeed logical with the behavior of my code on the mainframe, but not with its behavior on the PC. It's highly possible that the conversion from COBOL to C is the problem... That probably means I'll have to separate the big one-liner COMPUTEs into several individual COMPUTEs in order for the PC's results to reflect the mainframe's results.... Unless there is a better way. – Gael L Oct 04 '16 at 17:01
  • With the example from your comment, that is also a "bad" COMPUTE. If it is working (producing the correct result on the Mainframe) I'd suggest you'll find all the defined fields have excessive numbers of decimal places. – Bill Woodger Oct 04 '16 at 17:03
  • Oh. As soon as I posted my comment, I received your two comments, mister Woodger. So yes, in conclusion : I must decortiquate the big compute statements from the PC version of my code if I want them to give them mainframe results. Oh well. Thank you for your patience. – Gael L Oct 04 '16 at 17:03
  • Not really. First question is, is the result of that COMPUTE on the Mainframe correct? – Bill Woodger Oct 04 '16 at 17:04
  • That's the thing : the program I'm working on is a more-than-50 years old train simulator, and as I am a programmer (not an engineer), I don't have the right to call the Mainframe results bad. They probably are, because EVERY SINGLE variable from the original program is an integer. But my goal is to reproduce these (good?bad?) results on the PC version. – Gael L Oct 04 '16 at 17:07
  • Well, that's a bit of a pig, then. Best may be to post your question in the discussion area of the GnuCOBOL project, here: https://sourceforge.net/p/open-cobol/discussion/. Registering first is a good idea, else you may wait hours for someone with moderation powers to approve your post. – Bill Woodger Oct 04 '16 at 17:10
  • Yes, I shall probably do that. In the meanwhile, I'll do debug writes in the mainframe and pc code to see where the results differ, and ajust the PC code accordingly. Thank you, mister Woodger, for your help, and I am sorry if I sounded pretentious in my initial post. – Gael L Oct 04 '16 at 17:13
  • No worries. Breaking it up (correctly) will achieve your object, it's just not a nice thing to have to do. The Mainframe answer is "wrong", but that doesn't mean that 50 years ago it wasn't the result that the coder wanted, making it "right" but difficult to reproduce if they up and change the intermediates used :-) – Bill Woodger Oct 04 '16 at 17:17
  • "The STANDARD-DECIMAL phrase of the ARITHMETIC clause specifies that intermediate results be produced in a format that is described in an international standard, IEEE Std 754-2008, IEEE Standard for Floating-Point Arithmetic. The FLOAT-DECIMAL-34 phrase of the USAGE clause provides for a portable representation of data in that same format. The combination of these two features provides capabilities that exceed those of standard arithmetic in the previous COBOL standard." That's from a lateish draft of the 2014 Standard. – Bill Woodger Oct 04 '16 at 17:26
  • I supposed that the problem was related to the COBOL version, so before writing this post, I had recompiled my program using option -std=mvs, then -std=cobol85, but the result was still 188 instead of 100. – Gael L Oct 04 '16 at 17:29
  • Yes, I just tried that as well. Unfortunately, that option may not be for conformance with 1985 operation, but with syntax. – Bill Woodger Oct 04 '16 at 17:32
  • Alright. Seems that GNUCobol is too optimized in terms of arithmetic operations... Oh well. I just counted 25 occurences of "intermediate divisions" inside my code, so I better get to work. Good day to you ! – Gael L Oct 04 '16 at 19:09
  • 1
    `cobc -std=ibm` will do this "correctly wrong" in a future version and will add a warning option for this possible conversion issue, see https://sourceforge.net/p/open-cobol/feature-requests/175/ – Simon Sobisch Oct 09 '16 at 19:32
  • 1
    Oh ! I didn't expect that the developpers from GNUCobol would actually do something about it... I am surprised ! Of course, in the meanwhile, I already decorticated my calculations, but this feature will be nice should other people transfer old mainframe code to PC and expect same results. – Gael L Oct 11 '16 at 16:37
  • I found this: https://sourceforge.net/p/open-cobol/mailman/message/3868409/ which makes it seem like it is completely implementation defined (and thus not portable)... – Jerry Jeremiah May 24 '17 at 02:10

2 Answers2

2

https://lwn.net/Articles/733129/

This link mentions a new std option for gnuCobol 2.2: ibm-strict. I wonder if that would force the compute statement to do what you want.

George
  • 21
  • 3
0

Since it looks like the IBM truncates your values and GnuCobol does not, you may want to make use of GnuCobol functions to emulate the way IBM does it.

For positive integers at least, that's probably as simple as:

COMPUTE NUM-C = ((INTEGER(NUM-A / 100)) - (INTEGER(NUM-B / 100))) * 100

I haven't tested whether this works for negative integers since (1) the doco seems to indicate it rounds toward negative infinity rather than zero; (2) there doesn't appear to be a TRUNCATE function; and (3) I assumed you didn't care since you're data types aren't signed anyway.

If you do need to handle negatives, there are mathematical methods that will do it for you, I'd suggest asking it in a different question.

paxdiablo
  • 772,407
  • 210
  • 1,477
  • 1,841