Declaring External Decimal in Cobol - 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

Related

I can't figure out why my filler spaces aren't being displayed in my COBOL application

Ok so I'm making this application for school, which requires a certain format of spacing between the entries read in from .txt file. I've created the header using the filler term and the spacing works just fine, however when I apply the same method to the formatting of the records imported from the .txt it doesn't seem to work. I've tried everything under the sun and I can't make it work for the life of me!
This is what the output looks like now:
PARTNUMBER PARTNAME QUANTITY VALUE
1111111screws robertson 10 43210200Ajax
2222222screws robertson 08 41000100Ajax
2222233screws robertson 06 43210200Ajax
3333333screws robertson 04 41000100Ajax
4444444bolts dead 10 43210200Robo
5555555bolts dead 80 01000100Robo
But, it should be something like:
PARTNUMBER PARTNAME QUANTITY VALUE
1111111 screws robertson 10 43210200 Ajax
2222222 screws robertson 08 41000100 Ajax
2222233 screws robertson 06 43210200 Ajax
3333333 screws robertson 04 41000100 Ajax
4444444 bolts dead 10 43210200 Robo
5555555 bolts dead. 80 01000100 Robo
Below is the code that I think I need to make this happen, but again I'm just not sure why it isn't working
FILE SECTION.
FD INVENT-FILE-IN.
01 INVENT-RECORD-IN PIC X(49).
WORKING-STORAGE SECTION.
01 DISPLAY-HEADERS.
05 DISPLAY-PART-NUMBER PIC A(11)
VALUE "PARTNUMBER".
05 FILLER PIC X(1).
05 DISPLAY-PART-NAME PIC A(9)
VALUE "PARTNAME".
05 FILLER PIC X(4).
05 DISPLAY-QUANTITY PIC A(8)
VALUE "QUANTITY".
05 FILLER PIC X(2).
05 DISPLAY-VALUE PIC A(5)
VALUE "VALUE".
01 DISPLAY-RECORDS.
05 WS-INVENTORY-PART-NUMBER PIC 9(7).
05 FILLER PIC X(4) VALUE SPACES.
05 WS-INVENTORY-PART-NAME PIC X(20).
05 FILLER PIC X(4) VALUE SPACES.
05 WS-INVENTORY-QUANTITY PIC 9(4).
05 FILLER PIC X(2) VALUE SPACES.
05 WS-INVENTORY-VALUE PIC 9(8).
05 FILLER PIC X(1) VALUE SPACES.
05 WS-INVENTORY-SUPPLIER-CODE PIC X(5).
PROCEDURE DIVISION.
100-PROCESS-INVENTORY-FILE.
PERFORM 201-OPEN-INVENT-FILE.
PERFORM 202-DISPLAY-HEADER.
PERFORM 204-INPUT-INVENT-FILE
PERFORM 206-DISPLAY-RECORDS
UNTIL EOF-SWITCH = "Y".
PERFORM 205-TERMINATE-INVENTORY-FILE.
STOP RUN.
201-OPEN-INVENT-FILE.
OPEN INPUT INVENT-FILE-IN.
202-DISPLAY-HEADER.
DISPLAY DISPLAY-HEADERS.
206-DISPLAY-RECORDS.
MOVE INVENT-RECORD-IN TO DISPLAY-RECORDS.
DISPLAY DISPLAY-RECORDS.
READ INVENT-FILE-IN
AT END
MOVE "Y" TO EOF-SWITCH
NOT AT END
COMPUTE READ-COUNTER = READ-COUNTER + 1
END-READ.
204-INPUT-INVENT-FILE.
READ INVENT-FILE-IN
AT END
MOVE "Y" TO EOF-SWITCH
NOT AT END
COMPUTE READ-COUNTER = READ-COUNTER + 1
END-READ.
205-TERMINATE-INVENTORY-FILE.
CLOSE INVENT-FILE-IN.
As previously stated in the comments, in paragraph 206-DISPLAY-RECORDS, you are moving the entire input record to DISPLAY-RECORDS.
The problem here is that your input record is not formatted the same as your output record. This just means that you have to format it yourself. The easiest way to do this is to define your input input record differently. Something like this should do the trick:
FILE SECTION.
FD INVENT-FILE-IN.
01 INVENT-RECORD-IN.
05 INVENT-PART-NUMBER PIC 9(7).
05 INVENT-PART-NAME PIC X(20).
05 INVENT-QUANTITY PIC 9(4).
05 INVENT-VALUE PIC 9(8).
05 INVENT-SUPPLIER-CODE PIC X(5).
From here, its as easy as moving this fields to their equivalent spot in you DISPLAY-RECORDS:
206-DISPLAY-RECORDS.
MOVE INVENT-PART-NUMBER TO WS-INVENTORY-PART-NUMBER
MOVE INVENT-PART-NAME TO WS-INVENTORY-PART-NAME
MOVE INVENT-QUANTITY TO WS-INVENTORY-QUANTITY
MOVE INVENT-VALUE TO WS-INVENTORY-VALUE
MOVE INVENT-SUPPLIER-CODE TO WS-INVENTORY-SUPPLIER-CODE
READ INVENT-FILE-IN
AT END
MOVE "Y" TO EOF-SWITCH
NOT AT END
COMPUTE READ-COUNTER = READ-COUNTER + 1
END-READ.

Invalid type cast from 'null'

I am still new to COBOL and have been working on a project for school for almost a week now. I am running OpenCobol 1.1.
When I try to compile it I get this error.
typeck.c:5912: Invalid type cast from 'null'
Tag 1 0 Tag 2 10
Aborting compile of lab4.cob at line 214
I've been frustrated because I've tried changing the code around a lot with no luck.
Procedure Division.
000-Main.
Perform 100-initialize
Perform Until EndOfFile = "Y"
Read Lab4-in-File
At End
Move "Y" To EndOfFile
Not At End
Perform 300-process
End-Read
End-Perform
Perform 900-finalize
Stop Run.
100-intialize.
Perform 110-open-files
Perform 120-get-data.
110-open-files.
Open Input Lab4-in-File
Output Ot-File.
120-get-date.
Accept WS-date from date yyyymmdd
Move WS-Year To PH-Year
Move WS-Month To PH-Month
Move WS-Day To PH-Day.
300-process.
Move Dept-no To dl-dep-no
Move Employee-no To dl-emp-no
If First-name Not = "Null"
String First-name Delimited By Size
" " Delimited By Size
Last-name Delimited By Size
Into dl-emp
else
Move Last-name To dl-emp
End-If
Move Job-title To dl-job
Move DOH To dl-doh
Move Mar-status To dl-marital
Move Dependents To dl-dependents
Move MCoverage To dl-insurance
Move DCoverage To dl-insurance
Move VCoverage To dl-insurance
Move 401K To dl-401k
Move Pay-code To dl-pay-code
If Pay-code = "C" Or "S"
Compute Pay-hold rounded =
Pay / 12
Move Pay-hold To dl-monthly-pay
else
Compute Pay-hold rounded =
Pay * HPW * 4
Move Pay-hold To dl-monthly-pay
End-If
If Pay-code = "C"
Compute Com-hold rounded =
Act-sale * C-rate
Move Com-hold To dl-commission
Else
Move 0 To dl-commission
End-If
Perform 800-print
Multiply C-rate By Act-sale Giving
total-sales
Add Pay To total-sales.
800-print.
If LineNum > LinesPerPage
Perform 825-new-page
End-If
Write Lab4-Record2 From Detail-Line
**After advancing 1 line** *> This is line 214
Add 1 To LineNum.
825-new-page.
If PageNum > 0
Write Lab4-Record2 From Blank-line
After advancing 1 line
End-If
Add 1 To PageNum
Move PageNum To PH-PageNo
Write Lab4-Record2 From Page-Header
After advancing page
Write Lab4-Record2 From Blank-line
After advancing 1 line
Write Lab4-Record2 From Column-Header
After advancing 1 line
Write Lab4-Record2 From Blank-line
After advancing 1 line
Move 5 To LineNum.
900-finalize.
Perform 950-print-monthly-total
Perform 999-close-files.
950-print-monthly-total.
If LineNum + 1 > LinesPerPage
Perform 825-new-page
End-If
Write Lab4-Record2 From Blank-line
After advancing 1 line
Move total-sales To Total-pay
Write Lab4-Record2 From Total-Line
After advancing 1 line
Add 2 To LineNum.
999-close-files.
Close Lab4-in-File Ot-File.
I would really appreciate it if someone could help me find what is causing the error. Thanks in advance!
Working-Storage Section.
01 EndOfFile Pic X Value "N".
01 Report-fields.
05 PageNum Pic 9(3) value 0.
05 LinesPerPage Pic 9(2) value 40.
05 LineNum Pic 9(2) value 41.
01 WS-date.
05 WS-Year Pic 9(4).
05 WS-Month Pic 99.
05 WS-Day Pic 99.
01 total-fields.
05 total-sales Pic 9(11)v99 Value 0.
01 Page-Header.
05 PH-Month Pic Z9/.
05 PH-Day Pic 99/.
05 PH-Year Pic 9999.
05 Pic X(7) Value Spaces.
05 Pic X(29) Value "Stomper &" &
" Wombat's Emporium"
05 Pic X(6) Value "Page:".
05 PH-PageNo Pic ZZ9.
01 Column-Header.
05 Pic X(8) Value "Dep #".
05 Pic X(15) Value "Emp #".
05 Pic X(27) Value "Employee".
05 Pic X(18) Value "Title".
05 Pic X(9) Value "DOH".
05 Pic X(9) Value "Marital".
05 Pic X(7) Value "#Deps".
05 Pic X(6) Value "Ins".
05 Pic X(6) Value "401K".
05 Pic X(6) Value "Pay".
05 Pic X(27) Value "Expected " &
"Pay + Commission".
01 Pay-hold Pic 9(9)V9(2) Value 0.
01 Com-hold Pic 9(9)V9(2) Value 0.
01 Detail-Line.
05 dl-dep-no Pic X(5).
05 Pic X(1) Value spaces.
05 dl-emp-no Pic X(5).
05 Pic X(1) Value spaces.
05 dl-emp Pic X(35).
05 Pic X(1) Value spaces.
05 dl-job Pic X(20).
05 Pic X(1) Value spaces.
05 dl-doh Pic X(8).
05 Pic X(1) Value spaces.
05 dl-marital Pic X.
05 Pic X(1) Value spaces.
05 dl-dependents Pic 9(2).
05 Pic X(1) Value spaces.
05 dl-insurance Pic X(3).
05 Pic X(1) Value spaces.
05 dl-401k Pic Z.9ZZ.
05 Pic X(1) Value spaces.
05 dl-pay-code Pic X.
05 Pic X(1) Value spaces.
05 dl-monthly-pay Pic $$$$,$$$,$$9.99.
05 Pic X(1) Value spaces.
05 dl-commission Pic $$$,$$9.99.
01 Total-Line.
05 Pic X(61) Value Spaces.
05 Pic X(24) Value "Total" &
" Expected Payroll: ".
05 Total-pay Pic $$$$,$$$,$$$,$$9.99.
01 Blank-line Pic X Value spaces.
The compiler has a broken parser. It is broken because of a coding error (still a compiler bug). In cases like this you only have the chance to either spot the error or - much better - use a newer version.
I've just put your code in an online compiler using the last release of the compiler: GnuCOBOL 2.2. I highly suggest to upgrade to at least this version.
See your code here - I've just added a minimal header and end.
Then click on "Execute" and you compile it online, leading to the following error messages:
main.cobc: 27: error: duplicate PICTURE clause
main.cobc: 27: error: duplicate VALUE clause
main.cobc: 64: error: a Z or * which is after the decimal point cannot follow 9
If you check the line 27 in this program you see
05 Pic X(29) Value "Stomper &" &
" Wombat's Emporium" *> <<- missing period
05 Pic X(6) Value "Page:".
Fixed code is also available.

Reading space separated numbers in 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).

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