Reading space separated numbers in cobol - cobol

I am trying to solve a geometry problem using COBOL. The question requires me to read space separated integers(6 of them which stand for x and y coordinates of 3 points). Now the problem comes when I try to read them using ACCEPT. The numbers aren't read using space as a delimiter. I am using this
ACCEPT AX
ACCEPT AY
ACCEPT BX
ACCEPT BY1
ACCEPT X
ACCEPT Y
DISPLAY AX
DISPLAY AY
when I give an input of
1 2 2 1 2 2
AX contains 000012212
and AY contains 000000000.
All variables are of length 9.
The DATA-DIVISION
DATA DIVISION.
WORKING-STORAGE SECTION.
01 TP PIC X(126).
01 AX PIC S9(20).
01 AY PIC S9(20).
01 BX PIC S9(20).
01 BY1 PIC S9(20).
01 X PIC S9(20).
01 Y PIC S9(20).
01 T PIC S9(5).
01 ABC PIC S9(36).
01 ABD PIC S9(36).
01 CDA PIC S9(36).
01 CDB PIC S9(36).
This is the part where I accept the strings of coordinates.
ACCEPT TP.
DISPLAY TP
UNSTRING TP
DELIMITED BY ALL SPACE
INTO AX
AY
BX
BY1
X
Y

I suggest you ACCEPT all the input into 1 variable, and then break it down with a PERFORM UNTIL construction to get all the numbers right.

If you need to use ACCEPT, use UNSTRING to separate the data into fields and then validate those:
01 INPUT-COORDS PIC X(12).
01 XY-COORDS.
05 X-COORD-1 PIC 9.
05 Y-COORD-1 PIC 9.
05 X-COORD-2 PIC 9.
05 Y-COORD-2 PIC 9.
05 X-COORD-3 PIC 9.
05 Y-COORD-3 PIC 9.
ACCEPT INPUT-COORDS
MOVE SPACE TO XY-COORDS
UNSTRING INPUT-COORDS
DELIMITED BY ALL SPACE
INTO X-COORD-1
Y-COORD-1
X-COORD-2
Y-COORD-2
X-COORD-3
Y-COORD-3
ON OVERFLOW
do something meaningful
END-UNSTRING
Then validate the data thus extracted.
IF X-COORD-1 NOT NUMERIC, etc
I didn't check your input data. With only one-digit co-ordinates being valid, you can also consider this:
01 XY-COORDS.
05 X-COORD-1 PIC 9.
05 FILLER PIC X.
88 XYC-SEP1-OK VALUE SPACE.
05 Y-COORD-1 PIC 9.
05 FILLER PIC X.
88 XYC-SEP2-OK VALUE SPACE.
05 X-COORD-2 PIC 9.
05 FILLER PIC X.
88 XYC-SEP3-OK VALUE SPACE.
05 Y-COORD-2 PIC 9.
05 FILLER PIC X.
88 XYC-SEP4-OK VALUE SPACE.
05 X-COORD-3 PIC 9.
05 FILLER PIC X.
88 XYC-SEP5-OK VALUE SPACE.
05 Y-COORD-3 PIC 9.
ACCEPT XY-COORDS
Then do the same validation as above, plus check that the separators are each space (using the 88s).

Related

Best way to create a key-value "dict" in COBOL

I'm pretty new to Cobol, and got stuck trying to create something like a python dictionary, where we pass a key and the dictionary returns its value.
Python example:
>>> dict
{'AC': 'Acre', 'AL': 'Alagoas', 'AP': 'Amapa'}
>>> dict['AC']
'Acre'
I'm trying to do this in cobol, using redefines to create two arrays (one for the keys, other for the values).
I already created the arrays, but got stucked to associate these two arrays in a key-value function, once I can only access an array with integer values.
Here goes my data division, if someone can help with code samples.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
01 WRK-KEYS.
02 FILLER PIC X(2) VALUE "AC".
02 FILLER PIC X(2) VALUE "AL".
02 FILLER PIC X(2) VALUE "AP".
01 WRK-TABLE-KEYS REDEFINES WRK-KEYS.
02 WRK-KEY PIC X(2) OCCURS 3 TIMES.
01 WRK-VALUES.
02 FILLER PIC X(19) VALUE "Acre".
02 FILLER PIC X(19) VALUE "Alagoas".
02 FILLER PIC X(19) VALUE "Amapa".
01 WRK-TABLE-VALUES REDEFINES WRK-VALUES.
02 WRK-VALUE PIC X(10) OCCURS 3 TIMES.
You can use a table, as shown in the example below:
IDENTIFICATION DIVISION.
PROGRAM-ID. STATES.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 STATES-TABLE.
05 FILLER PIC X(2) VALUE "AC".
05 FILLER PIC X(7) VALUE "Acre ".
05 FILLER PIC X(2) VALUE "AL".
05 FILLER PIC X(7) VALUE "Alagoas".
05 FILLER PIC X(2) VALUE "AP".
05 FILLER PIC X(7) VALUE "Amapá ".
01 RDF-STATES-TABLE REDEFINES STATES-TABLE.
05 STATE-GROUP OCCURS 3 TIMES.
10 STATE-CODE PIC X(2).
10 STATE-NAME PIC X(7).
PROCEDURE DIVISION.
DISPLAY "STATES : "STATES-TABLE.
DISPLAY 'STATE-CODE(1) : ' STATE-CODE(1).
DISPLAY 'STATE-NAME(1) : ' STATE-NAME(1).
DISPLAY 'STATE-CODE(2) : ' STATE-CODE(2).
DISPLAY 'STATE-NAME(2) : ' STATE-NAME(2).
DISPLAY 'STATE-CODE(3) : ' STATE-CODE(3).
DISPLAY 'STATE-NAME(3) : ' STATE-NAME(3).
STOP RUN.
Resulting in:
$ ./states
STATES : ACAcre ALAlagoasAPAmapá
STATE-CODE(1) : AC
STATE-NAME(1) : Acre
STATE-CODE(2) : AL
STATE-NAME(2) : Alagoas
STATE-CODE(3) : AP
STATE-NAME(3) : Amapá
Remember that á uses two bytes in UTF-8.

Declaring External Decimal in Cobol

Looking for solution on my problem. The values I need to convert was in alphanumeric.
05 WS-NUM-TX.
05 WS-NUM PIC P9(04).
05 WS-NUM1 PIC P9(03).
05 WS-NUM2 PIC P9(03).
MOVE '0001 222217' TO WS-NUM-TX.
MOVE WS-NUM-TX(1:4) TO WS-NUM.
MOVE WS-NUM-TX(6:3) TO WS-NUM1.
MOVE WS-NUM-TX(9:3) TO WS-NUM2.
I did COMPUTE WS-NUM = FUNCTION NUMVAL(WS-NUM-TX) for this to be numeric.
Now, the problem is, I need this values as decimal for computation. Need help to convert this values to become .0001, .222 and .217 however the declaration
I did for external decimal displayed with no decimal point. Please help. Thank You.
The P in the PICTURE clause is an error, as is the absence of a PICTURE clause for WS-NUM-TX. (As of the the 4th revision.)
Possibly,
05 WS-NUM-TX PIC X(11).
05 WS-NUM PIC .9(04).
05 WS-NUM1 PIC .9(03).
05 WS-NUM2 PIC .9(03).
MOVE '0001 222217' TO WS-NUM-TX.
COMPUTE WS-NUM = FUNCTION NUMVAL (WS-NUM-TX(1:4)) / 10000.
COMPUTE WS-NUM1 = FUNCTION NUMVAL (WS-NUM-TX(6:3)) / 1000.
COMPUTE WS-NUM2 = FUNCTION NUMVAL (WS-NUM-TX(9:3)) / 1000.
Based on the original post (revisions 1 and 2) with additional editing.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 FILLER.
05 WS-NUM-TX.
07 WS-NUM-TX1 PIC 9(4).
07 FILLER PIC X.
07 WS-NUM-TX2 PIC 9(3).
07 WS-NUM-TX3 PIC 9(3).
05 WS-NUM PIC .9(04).
05 WS-NUM1 PIC .9(03).
05 WS-NUM2 PIC .9(03).
PROCEDURE DIVISION.
BEGIN.
MOVE '0001 222217' TO WS-NUM-TX.
DIVIDE WS-NUM-TX1 BY 10000 GIVING WS-NUM.
DIVIDE WS-NUM-TX2 BY 1000 GIVING WS-NUM1.
DIVIDE WS-NUM-TX3 BY 1000 GIVING WS-NUM2.
DISPLAY WS-NUM
DISPLAY WS-NUM1
DISPLAY WS-NUM2
STOP RUN
.
Output:
.0001
.222
.217
Use V in the picture clause. This determines the position of the comma.
For example: PIC 9(05)V9(03) will result in 00000,000
Source: https://www.ibm.com/support/knowledgecenter/en/SS6SG3_4.2.0/com.ibm.entcobol.doc_4.2/PGandLR/ref/rlddesym.htm

COBOL COMPUTE decimal values from a file

Trying to understand compute. Would it be correct to calculate the sum of the earned credits using FSemesterTotal which is a PIC 99V99 like this? COMPUTE FSemesterTotal = Earned + Earned. I think there is supposed to be a counter in my loop to check if i read in the first earned value so i can add it to the second value coming in not sure how to accomplish this in COBOL.
Currently my input is like this,
CMPS161 ALGORITHM DSGN/IMPLMNT I A 3.00
ENGL322 INTRO TO PROF/TECH WRITING A 3.00
MATH241 ELEM STATISTICS B 3.00
ART 106 SURV WORLD ART HIST II A 3.00
BIOL152 GENERAL BIOL LAB I B 1.00
CMPS257 DISCRETE STRUCTURE A 3.00
CMPS28O ALGORITHM DSGN/IMPLEM II B 3.00
CMPS290 COMPUTER ORGANIZATION A 3.00
CMPS390 DATA STRUCTURES B 3.00
GBIO153 GENERAL BIOL II B 3.00
CMPS294 INTERNET PROGRAMMING B 3.00
CMPS315 SYSTEM ADMINISTRATION A 3.00
CMPS329 COMPUTER NETWORKING SECURITY A 3.00
CMPS383 INFORMATION SYSTEMS A 3.00
CMPS415 INTERGRATED TECH SYSTEMS B 3.00
COBOL CODE
IDENTIFICATION DIVISION.
PROGRAM-ID. P2.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT myInFile ASSIGN TO "P2In.dat".
SELECT myOutFile ASSIGN TO "P2Out.dat".
DATA DIVISION.
FILE SECTION.
FD myInFile.
01 inRecord.
02 Course PIC X(13).
02 Title PIC X(32).
02 Grade PIC X(4).
02 Earned PIC 9V99.
02 FILLER PIC X(3).
FD myOutFile.
01 outRecord.
02 myCourse PIC X(13).
02 myTitle PIC X(32).
02 myGrade PIC X(4).
02 myEarned PIC 9.99.
02 FILLER PIC X(3).
WORKING-STORAGE SECTION.
01 E0F PIC X(3) VALUE "NO ".
01 NAME-HDR.
05 FILLER PIC X(10) VALUE "NAME HERE ".
01 SCHOOLID-HDR.
05 FILLER PIC X(8) VALUE "SCHOOLID"
01 COLUMN-HDR.
05 CCourse PIC X(6) VALUE "COURSE".
05 CSpace PIC X(7) VALUE SPACES.
05 HTitle PIC X(5) VALUE "TITLE".
05 HSpace PIC X(27) VALUE SPACES.
05 CGrade PIC XX VALUE "GR".
05 CSpace PIC XXX VALUE SPACES.
05 CEarned PIC X(6) VALUE "EARNED".
05 QSpace PIC X(4) VALUE SPACES.
05 Qpts PIC X(4) VALUE "Qpts".
01 FOOTER-SMS.
05 FSemester PIC X(28) VALUE " SEMESTER".
05 FSpaces PIC x(21) VALUE SPACES.
05 FSemesterTotal PIC 99V99.
01 FOOTER-CUMUL.
05 FCumulative PIC X(30) VALUE" CUMULATIVE".
05 FSpaces PIC X(19) VALUE SPACES.
05 FCumulTotal PIC 99V99.
01 QPTS-VAL.
05 QSpace PIC X(5) VALUE SPACES.
05 QPtsValue PIC 99V99.
01 GPA.
05 GSpace PIC XX VALUE SPACES.
05 GpaScore PIC 9.99.
PROCEDURE DIVISION.
MAIN-PROGRAM.
PERFORM HEADER.
PERFORM FILE-IO.
PERFORM CLOSING.
STOP RUN.
HEADER.
OPEN INPUT myInFile
OUTPUT myOutFile.
WRITE outRecord FROM NAME-HDR.
WRITE outRecord FROM SCHOOLID-HDR
AFTER ADVANCING 1 LINE.
WRITE outRecord FROM COLUMN-HDR
AFTER ADVANCING 2 LINES.
MOVE SPACES TO outRecord.
WRITE outRecord
AFTER ADVANCING 1 LINE.
FILE-IO.
READ myInFile
AT END
MOVE "YES" TO EOF.
DISPLAY NAME-HDR.
DISPLAY SCHOOLID-HDR.
DISPLAY SPACES.
DISPLAY SPACES.
DISPLAY "FALL 2014"
DISPLAY COLUMN-HDR.
PERFORM PROCESS-RECORD
UNTIL EOF = "YES".
PROCESS-RECORD.
MOVE Course to myCourse.
MOVE Title to myTitle.
MOVE Grade to myGrade.
MOVE Earned to myEarned.
WRITE outRecord
AFTER ADVANCING 1 LINE.
READ myInFile
AT END
MOVE "YES" TO EOF.
NOT AT END
IF myCourse = "ART 106 " THEN
DISPLAY FOOTER-SMS, QPTS-VAL, GPA
DISPLAY FOOTER-CUMUL, QPTS-VAL, GPA
DISPLAY SPACES.
DISPLAY "SPRING 2015"
END-IF.
IF myCourse = "CMPS285 " THEN
DISPLAY FOOTER-SMS, QPTS-VAL, GPA
DISPLAY FOOTER-CUMUL, QPTS-VAL, GPA
DISPLAY SPACES.
DISPLAY "FALL 2015"
END-IF.
IF myCourse = "CMPS294 " THEN
DISPLAY FOOTER-SMS, QPTS-VAL, GPA
DISPLAY FOOTER-CUMUL, QPTS-VAL, GPA
DISPLAY SPACES.
DISPLAY "SPRING 2016"
END-IF.
CLOSING.
DISPLAY FOOTER-SMS, QPTS-VAL, GPA.
DISPLAY FOOTER-CUMUL, QPTS-VAL, GPA.
CLOSE myInFile
myOutFile.
The question was: "Can I use COMPUTE this way?"
The answer is:
Yes, but you likely want to add a ON SIZE ERROR to cater for a possible size overflow, just in case your input data has too many entries.
If the question behind the question is: "Will the program work?"
The answer is no:
Despite the issues Brian already pointed out: you'll need a de-editing to change the data from 9.99 (4 bytes, not usable for arithmetic) to 9v99 (3 bytes, usable for arithmetic.
And if you don't use an ISAM file which is validated by the runtime: Always validate file input (the file may be broken and you likely don't want to abend or produce wrong results).

Cobol, Finding a Percentage

I'm working on an assignment for my class, and I'm having an issue with getting a percentage to show the proper value for my COBOL Lab.
My issue is with PERCENT-DISCOUNT / WS-PERCENT-WITH-DISCOUNT (at least, I believe it is).
When the program is run, I get the result 50.0. The result I should be getting (assuming I did the math correctly by hand) is 55.6. I'm not too sure where I'm going wrong.
Here is the code that I currently have written for the program.
*
IDENTIFICATION DIVISION.
PROGRAM-ID. LAB2.
AUTHOR. XXXXXXX XXXXXXXXXXX.
*
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT IPT-FILE ASSIGN TO 'LAB2.DAT'
ORGANIZATION IS LINE SEQUENTIAL.
SELECT PRT-FILE ASSIGN TO 'LAB2_OUTPUT.DAT'
ORGANIZATION IS LINE SEQUENTIAL.
*
DATA DIVISION.
FILE SECTION.
*
FD IPT-FILE
RECORD CONTAINS 80 CHARACTERS
RECORDING MODE IS F
DATA RECORD IS INPUT-RECORD.
01 IPT-RECORD.
05 IPT-INV-NUMBER PIC 9(04).
05 IPT-INV-QUANTITY PIC 9(03).
05 IPT-INV-DESCRIPTION PIC X(13).
05 IPT-INV-UNITPRICE PIC 9999V99.
05 IPT-INV-PROD-CLASS PIC 9(01).
*
FD PRT-FILE
RECORD CONTAINS 132 CHARACTERS
RECORDING MODE IS F
DATA RECORD IS PRT-LINE.
01 PRT-LINE.
05 FILLER PIC X(04).
05 PRT-INV-NUMBER PIC 9(04).
05 FILLER PIC X(02).
05 PRT-EXTENDED-PRICE PIC Z,ZZZ,ZZ9.99.
05 FILLER PIC X(04).
05 PRT-DISCOUNT-AMOUNT PIC ZZZ,ZZ9.99.
05 FILLER PIC X(03).
05 PRT-NET-PRICE PIC Z,ZZZ,ZZ9.99.
05 FILLER PIC X(10).
05 PRT-PRODUCT-CLASS PIC 9.
05 FILLER PIC X(07).
05 PRT-TRANS-PERCENT PIC Z9.9.
05 FILLER PIC X(05).
05 PRT-TRANS-CHARGE PIC ZZZ,ZZ9.99.
*
WORKING-STORAGE SECTION.
*
01 EOF-SWITCH PIC X VALUE 'N'.
*
01 WS-EXTENDED-COST PIC 9(07)V99.
01 WS-DISCOUNT-AMOUNT PIC 9(06)V99.
01 WS-NET-COST PIC 9(07)V99.
01 WS-TRANS-PERCENT PIC ZZ9V9.
01 WS-TRANS-COST PIC 9(06)V99.
*
01 WS-TOTAL-EXTENDED-COST PIC 9(09)V99 VALUE ZERO.
01 WS-TOTAL-NET-COST PIC 9(08)V99 VALUE ZERO.
01 WS-TOTAL-TRANS-COST PIC 9(08)V99 VALUE ZERO.
01 WS-TOTAL-WITH-DISCOUNT PIC 99V9 VALUE ZERO.
01 WS-TOTAL-ITEMS PIC 99V9 VALUE ZERO.
01 WS-PERCENT-WITH-DISCOUNT PIC 99V9.
01 WS-TOTAL-NO-DISCOUNT PIC 99V9 VALUE ZERO.
*
01 HEADING-NAME.
05 MY-NAME PIC X(20) VALUE
'XXXXX XXXXXXX, LAB 2'.
01 HEADING-COLUMN-1.
05 FILLER PIC X(05) VALUE SPACES.
05 COLUMN-1-INV-NUM PIC X(03) VALUE 'INV'.
05 FILLER PIC X(06) VALUE SPACES.
05 COLUMN-1-EXT-PRC PIC X(08) VALUE 'EXTENDED'.
05 FILLER PIC X(06) VALUE SPACES.
05 COLUMN-1-DISC-AMT PIC X(08) VALUE 'DISCOUNT'.
05 FILLER PIC X(06) VALUE SPACES.
05 COLUMN-1-NET-PRC PIC X(09) VALUE 'NET PRICE'.
05 FILLER PIC X(06) VALUE SPACES.
05 COLUMN-1-CLASS PIC X(05) VALUE 'CLASS'.
05 FILLER PIC X(06) VALUE SPACES.
05 COLUMN-1-TRANS-P PIC X(05) VALUE 'TRANS'.
05 FILLER PIC X(06) VALUE SPACES.
05 COLUMN-1-TRANS-C PIC X(14) VALUE 'TRANSPORTATION'.
01 HEADING-COLUMN-2.
05 FILLER PIC X(05) VALUE SPACES.
05 COLUMN-2-INV-NUM PIC X(03) VALUE 'NUM'.
05 FILLER PIC X(09) VALUE SPACES.
05 COLUMN-2-EXT-PRC PIC X(08) VALUE 'PRICE'.
05 FILLER PIC X(05) VALUE SPACES.
05 COLUMN-2-DISC-AMT PIC X(08) VALUE 'AMOUNT'.
05 FILLER PIC X(06) VALUE SPACES.
05 COLUMN-2-NET-PRC PIC X(09) VALUE SPACES.
05 FILLER PIC X(06) VALUE SPACES.
05 COLUMN-2-CLASS PIC X(05) VALUE SPACES.
05 FILLER PIC X(08) VALUE SPACES.
05 COLUMN-2-TRANS-P PIC X(05) VALUE '%'.
05 FILLER PIC X(05) VALUE SPACES.
05 COLUMN-2-TRANS-C PIC X(14) VALUE 'CHARGE'.
01 BLANK-LINE.
05 BLANK-SPACE PIC X VALUE SPACES.
01 TOTAL-FOOTER.
05 FILLER PIC X(07) VALUE SPACES.
05 TOTAL-EXTENDED-COST PIC $$$$,$$$,$$9.99.
05 FILLER PIC X(15) VALUE SPACES.
05 TOTAL-NET-COST PIC $$$,$$$,$$9.99.
05 FILLER PIC X(23) VALUE SPACES.
05 TOTAL-TRANS-COST PIC $$$,$$$,$$9.99.
01 TOTAL-NO-DISCOUNT-FOOTER.
05 TOTAL-SENTENCE PIC X(31) VALUE
'TOTAL ITEMS WITHOUT DISCOUNT = '.
05 TOTAL-NO-DISCOUNT PIC Z9.
01 PERCENT-DISCOUNT-FOOTER.
05 PERCENT-SENTENCE PIC X(44) VALUE
'PERCENT OF ITEMS THAT RECEIVED A DISCOUNT = '.
05 PERCENT-DISCOUNT PIC Z9.9.
*
PROCEDURE DIVISION.
*
OPEN INPUT IPT-FILE.
OPEN OUTPUT PRT-FILE.
*
WRITE PRT-LINE FROM HEADING-NAME AFTER ADVANCING 0 LINES.
WRITE PRT-LINE FROM HEADING-COLUMN-1 AFTER ADVANCING 3 LINES.
WRITE PRT-LINE FROM HEADING-COLUMN-2 AFTER ADVANCING 1 LINES.
WRITE PRT-LINE FROM BLANK-LINE AFTER ADVANCING 1 LINES.
*
READ IPT-FILE AT END MOVE 'Y' TO EOF-SWITCH.
*
PERFORM MAIN-LOOP UNTIL EOF-SWITCH EQUALS 'Y'.
*
DIVIDE WS-TOTAL-WITH-DISCOUNT BY WS-TOTAL-ITEMS
GIVING WS-PERCENT-WITH-DISCOUNT.
MULTIPLY WS-PERCENT-WITH-DISCOUNT BY 100
GIVING WS-PERCENT-WITH-DISCOUNT.
*
MOVE WS-TOTAL-EXTENDED-COST TO TOTAL-EXTENDED-COST.
MOVE WS-TOTAL-NET-COST TO TOTAL-NET-COST.
MOVE WS-TOTAL-TRANS-COST TO TOTAL-TRANS-COST.
MOVE WS-TOTAL-NO-DISCOUNT TO TOTAL-NO-DISCOUNT.
MOVE WS-PERCENT-WITH-DISCOUNT TO PERCENT-DISCOUNT.
*
WRITE PRT-LINE FROM TOTAL-FOOTER AFTER ADVANCING 3 LINES.
WRITE PRT-LINE FROM TOTAL-NO-DISCOUNT-FOOTER AFTER
ADVANCING 3 LINES.
WRITE PRT-LINE FROM BLANK-LINE AFTER ADVANCING 1 LINES.
WRITE PRT-LINE FROM PERCENT-DISCOUNT-FOOTER AFTER ADVANCING
1 LINES.
*
CLOSE IPT-FILE, PRT-FILE.
STOP RUN.
*
MAIN-LOOP.
MOVE SPACES TO PRT-LINE.
*
MULTIPLY IPT-INV-QUANTITY BY IPT-INV-UNITPRICE
GIVING WS-EXTENDED-COST ROUNDED.
MOVE WS-EXTENDED-COST TO PRT-EXTENDED-PRICE.
*
ADD 1 TO WS-TOTAL-ITEMS
*
IF WS-EXTENDED-COST IS GREATER THAN 200 THEN
MULTIPLY WS-EXTENDED-COST BY 0.11 GIVING
WS-DISCOUNT-AMOUNT ROUNDED
ADD 1 TO WS-TOTAL-WITH-DISCOUNT
*
ELSE
MOVE ZERO TO WS-DISCOUNT-AMOUNT
ADD 1 TO WS-TOTAL-NO-DISCOUNT
END-IF.
*
IF IPT-INV-PROD-CLASS IS EQUAL TO 1 THEN
MOVE 27.0 TO WS-TRANS-PERCENT
MULTIPLY WS-EXTENDED-COST BY 0.27 GIVING
WS-TRANS-COST ROUNDED
*
ELSE IF IPT-INV-PROD-CLASS IS EQUAL TO 2 THEN
MOVE 17.0 TO WS-TRANS-PERCENT
MULTIPLY WS-EXTENDED-COST BY 0.17 GIVING
WS-TRANS-COST ROUNDED
*
ELSE IF IPT-INV-QUANTITY IS GREATER THAN 100 THEN
MOVE 13.5 TO WS-TRANS-PERCENT
MULTIPLY WS-EXTENDED-COST BY 0.135 GIVING
WS-TRANS-COST ROUNDED
*
ELSE
MOVE ZERO TO WS-TRANS-PERCENT
MOVE 25.00 TO WS-TRANS-COST
END-IF.
*
SUBTRACT WS-EXTENDED-COST FROM WS-DISCOUNT-AMOUNT
GIVING WS-NET-COST.
ADD WS-EXTENDED-COST TO WS-TOTAL-EXTENDED-COST.
ADD WS-NET-COST TO WS-TOTAL-NET-COST.
ADD WS-TRANS-COST TO WS-TOTAL-TRANS-COST.
*
MOVE IPT-INV-NUMBER TO PRT-INV-NUMBER.
MOVE WS-EXTENDED-COST TO PRT-EXTENDED-PRICE.
MOVE WS-DISCOUNT-AMOUNT TO PRT-DISCOUNT-AMOUNT.
MOVE WS-NET-COST TO PRT-NET-PRICE.
MOVE IPT-INV-PROD-CLASS TO PRT-PRODUCT-CLASS.
MOVE WS-TRANS-PERCENT TO PRT-TRANS-PERCENT.
MOVE WS-TRANS-COST TO PRT-TRANS-CHARGE.
*
WRITE PRT-LINE AFTER ADVANCING 1 LINES.
*
READ IPT-FILE AT END MOVE 'Y' TO EOF-SWITCH.
Here is the information that the .dat file holds.
2047105TYPEWRITER 0800002
1742010HANDLE 0010001
2149150USB DRIVE 1200003
3761005TAPE 5000004
2791010BOLTS 0000751
3000100STAPLER 0002007
3001101OVERHEAD PROJ0099997
3002099PENCILS 0000097
4001184CANADIAN RUGS0150294
4003050CARPET 0040000
4005001WASTE BASKETS0003793
5001010HINGES 0010001
5003010PENS (GOLD) 0049992
5004400PENS (BLACK) 0002004
8888999HIGH CHAIR 9999991
8889412PLAY PEN 0074992
0001001LOW TEST 0000019
9999999LAST RECORD 0000011
Your problem is here:
01 WS-PERCENT-WITH-DISCOUNT PIC 99V9.
When you do your divide, you store the result in that field. If you expect it to be 0.556, what you are storing is 0.5, because you have only defined one decimal place, so the two low-order decimal places are simply truncated.
When you then multiply by 100, you make that 50.0.
If you define that field with three decimal places, your expected answer should appear (I've not checked your data).
However, a better way to do it is to define more integer digits, so that the field is large enough to hold your intermediate result and multiply by 100 first. Then you can divide (and you may want to consider ROUNDED on that, but it depends on the spec for the program).
There are a few questions here on problems with COMPUTE. Reading those questions and understanding the answers will help you get a good grasp on this. In COBOL, you define the accuracy you require, and you do that by supplying the correct number of integer and decimal digits.
You could also look through some of the other COBOL questions, where you'll find lots of advice on using FILE STATUS on your files, and checking the result of each IO. You can also use the file-status field you define to check for end-of-file, rather than using AT END/NOT AT END: you should find that it needs less code, and is more easy to understand.
Ditch as many full-stops/periods as you can. You need one at the end of the PROCEDURE DIVISION header, one at the end of a procedure-name, one at the end of a procedure, and one at the end of the program (if you have no procedure-names). All the others are superfluous. Commas in code tend to distract, you may find it clearer to use indentation and formatting of the statements.
Whilst it is well-constructed, your nested-IF would be better as an EVALUATE.
You have many constants in your program. It is better to define those as data-items, with a well-chosen name, so that the code "reads", and no-one has to wonder about the significance of 0.27. You also have examples where you have two constants which are obviously related, 27.0 and 0.27 for instance, which are better served by just being one thing. If someone "maintains" the program, they may only change one of the values without changing the other (not expecting there to be another).
Look also at the use of 88-level condition names. The "switch = y" can become "end-of-invoice-file" for instance, and that 88 can be on the file-status for that file, with a value of "10".
You should test your program with an empty input file, and see if you like the results.
As a beginner with COBOL, it is not a bad shot at all.

COBOL File input, numbers separated with space

I am a newbie of COBOL, I am facing the following problem.
I have a input file with content:
2 3 2 4
4 numbers are in the same row and separated with exactly one space.
the 4 numbers can be in 1 digit, 2 digit and 3 digit
Can I put those 4 numbers to 4 variables with PIC?
such as: PIC XXX XXX XXX XXX (This is not working.)
currently I am using substring to achieve the task, but this is not efficient and messy, is there any other way i can finish the task easily?
Thanks
You can do this by two ways. Number one is to use unstring sentence. Or you can declare a variable level 01 and define in it every variable of the string separately.
For example:
01 WS-FILE.
05 WS-FIELD-01 PIC 9.
05 FILLER PIC X.
05 WS-FIELD-02 PIC 9.
05 FILLER PIC X.
05 WS-FIELD-03 PIC 9.
05 FILLER PIC X.
05 WS-FIELD-04 PIC 9.
05 FILLER PIC X.
And when you read the file use:
READ FILE INTO WS-FILE.
You can use an UNSTRING function (i dont know if you refer to that with substring)
UNSTRING WS-FILE-RECORD DELIMITED BY SPACE
INTO WS-FIELD1
WS-FIELD2
WS-FIELD3
WS-FIELD4
END-UNSTRING
with this if you have:
WS-FILE-RECORD="1 2 3 4"
WS-FIELD1 = "1"
WS-FIELD2 = "2"
WS-FIELD3 = "3"
WS-FIELD4 = "4"
or if you have:
WS-FILE-RECORD="1 22 333 4444"
WS-FIELD1 = "1"
WS-FIELD2 = "22"
WS-FIELD3 = "333"
WS-FIELD4 = "4444"
01 YOUR-NUMBERS.
03 YOUR-NUMBER PIC 9(04) OCCURS 4.
01 INDEX-YOUR-NUMBERS PIC 9(01).
01 YOUR-RECORD.
03 YOUR-RECORD-4.
05 YOUR-RECORD-4-NUM PIC X(04).
05 FILLER PIC X(01).
05 YOUR-RECORD-4-REST.
07 FILLER PIC X(09).
07 YOUR-RECORD-4-END PIC X(05).
03 YOUR-RECORD-3 REDEFINES YOUR-RECORD-4.
05 YOUR-RECORD-3-NUM PIC X(03).
05 FILLER PIC X(01).
05 YOUR-RECORD-3-REST.
07 FILLER PIC X(11).
07 YOUR-RECORD-3-END PIC X(04).
03 YOUR-RECORD-2 REDEFINES YOUR-RECORD-4.
05 YOUR-RECORD-2-NUM PIC X(02).
05 FILLER PIC X(01).
05 YOUR-RECORD-2-REST.
07 FILLER PIC X(13).
07 YOUR-RECORD-2-END PIC X(03).
03 YOUR-RECORD-1 REDEFINES YOUR-RECORD-4.
05 YOUR-RECORD-1-NUM PIC X(01).
05 FILLER PIC X(01).
05 YOUR-RECORD-1-REST.
07 FILLER PIC X(15).
07 YOUR-RECORD-1-END PIC X(02).
MOVE SPACES TO YOUR-RECORD.
READ YOUR-RECORD.
PERFORM 0100-FIND-NUMBERS
VARYING INDEX-YOUR-NUMBERS
FROM 1
TO 4.
0100-FIND-NUMBERS.
IF YOUR-RECORD-4-NUM IS NUMERIC
MOVE YOUR-RECORD-4-NUM TO YOUR-NUMBER(INDEX-YOUR-NUMBERS)
MOVE YOUR-RECORD-4-REST TO YOUR-RECORD-4
MOVE SPACES TO YOUR-RECORD-4-END
ELSE
IF YOUR-RECORD-3-NUM IS NUMERIC
MOVE YOUR-RECORD-3-NUM TO YOUR-NUMBER(INDEX-YOUR-NUMBERS)
MOVE YOUR-RECORD-3-REST TO YOUR-RECORD-4
MOVE SPACES TO YOUR-RECORD-3-END
ELSE
IF YOUR-RECORD-2-NUM IS NUMERIC
MOVE YOUR-RECORD-2-NUM TO YOUR-NUMBER(INDEX-YOUR-NUMBERS)
MOVE YOUR-RECORD-2-REST TO YOUR-RECORD-4
MOVE SPACES TO YOUR-RECORD-2-END
ELSE
MOVE YOUR-RECORD-1-NUM TO YOUR-NUMBER(INDEX-YOUR-NUMBERS)
MOVE YOUR-RECORD-1-REST TO YOUR-RECORD-4
MOVE SPACES TO YOUR-RECORD-1-END.
Here's a way to do it. Maybe not a good way. Maybe not an efficient way. Maybe not an easy way. But certainly a way that doesn't involve string/unstring - using PIC only. ish.
You could create a little state machine that ran through and calculated every number as it goes. There are many advantages to approaching things on a character by character basis for parsing. The code is usually very simple, especially with a simple regex like number or whitespace.
Identification Division.
Program-ID. PARSENUM.
Data Division.
Working-Storage Section.
01 II comp-5 pic s9(8) value 0.
01 Num-Val comp-5 pic s9(8) value 0.
01 In-Str pic x(80).
01 In-Ch pic 9.
01 pic x(1).
88 In-Number value 'N'.
88 In-Whitespace value 'W'.
Procedure Division.
*> Fake up some data...
Move '1 212 303 44 5678 6 75 888 976' to In-Str
*> Parse Numbers
Set In-Whitespace to true
Perform varying II from 1 by 1
until II > Length of In-Str
If In-Str (II:1) is numeric
Move In-Str (II:1) to In-Ch
Evaluate true
when In-Whitespace
Compute Num-Val = In-Ch
Set In-Number to true
when In-Number
Compute Num-Val = (Num-Val * 10) + In-Ch
End-Evaluate
Else
If In-Number
Display 'Found Number: ' Num-Val
Set In-Whitespace to true
End-If
End-If
End-Perform
Goback.
You should get output that looks like:
Found Number: +0000000001
Found Number: +0000000212
Found Number: +0000000303
Found Number: +0000000044
Found Number: +0000005678
Found Number: +0000000006
Found Number: +0000000075
Found Number: +0000000888
Found Number: +0000000976

Resources