How to convert a alphanumeric string into numeric decimal in COBOL - cobol

For eg., i have alphanumeric string 'ABCDEF 0 0.450' and i need to get '0.450' as numeric decimal and do arithmetic on it. Do we have a way? Please suggest.

I assume the format is not fixed, since then you could just pick that part of the string and move it to a field defined with pic 9.999 and use it from there.
This assumes the string value is made up of 3 parts separated by spaces. First define some fields:
1 part1 pic x(14).
1 part2 pic x(14).
1 part3 pic x(07).
1 digitsAn.
2 digits pic 9(03).
1 decimalsAn.
2 decimals pic .9(03).
1 theValue pic 9(03)v9(3).
The An suffix is from my naming convention, and indicates a alphanumeric value. If the string input may be longer increase the sizes as needed.
The rest of the code parses theString into theValue.
* Extract the third part.
initialize part3
unstring theString delimited by all spaces into part1 part2 part3
* make sure the receiving field contains numeric data.
inspect part3 replacing spaces by zeroes
* extract digits before and after the decimal point.
unstring part3 delimited by "." into digits decimalsAn(2:)
* Combine parts before and after decimal points into a numeric value.
compute theValue = digits + decimals
Beware, I haven't run this through a compiler!

Asusually, I could find out a way to achieve it!
As said above, UNSTRINGing and combining didnt work, but REDEFINES works!
Get alphanumeric string redefined into two numeric fields to hold and process decimal part and integer part individually. Atlast divide the decimal-total by 1000 and add it to integer-part. Thats it! Code snippets follow...
01 WS-NUM-STR PIC X(14) VALUE 'ABCDEF 0 0.450'.
01 WS-NUM-STR-MOD REDEFINES WS-NUM-STR.
05 FILLER PIC X(9).
05 WS-INT-PART PIC 9.
05 FILLER PIC X.
05 WS-DEC-PART PIC 999.
----------
----------
* CODE TO ADD ALL INTEGER PARTS
* CODE TO ADD ALL DECIMAL PARTS
COMPUTE = INTEGER-TOTAL + (DECIMAL-TOTAL / 1000)
Note: As I already knew that decimal part has 3 digits, I divided DECIMAL-TOTAL by 1000. Hope you could figure out.
And Please suggest any other way to achieve the same!

In most modern Cobol compilers, you can move a numeric-edited field to a numeric field.
Borrowing from your example below:
01 WS-NUM-STR PIC X(14) VALUE 'ABCDEF 0 0.450'.
01 WS-NUM-STR-MOD REDEFINES WS-NUM-STR.
05 FILLER PIC X(9).
05 WS-EDITED-NUMBER PIC 9.999.
01 WS-NUMBER PIC 9V999.
----------
MOVE WS-EDITED-NUMBER TO WS-NUMBER
And then, do the calculation with WS-NUMBER.

For eg., i have alphanumeric string
'ABCDEF 0 0.450' and i need to get
'0.450' as numeric decimal and do
arithmetic on it. Do we have a way?
Please suggest.
If you are running in CICS, you could use the BIF DEEDIT function -- that will leave you with "00.450".
If you are not running in CICS, assume you have your string in a field called WS-STR:
Move 0 to JJ
Perform varying II from 1 by 1
until II > Length of WS-STR
Evaluate WS-STR (II:1)
when '0'
when '1'
when '2'
when '3'
when '4'
when '5'
when '6'
when '7'
when '8'
when '9'
when '.'
Add 1 to JJ
Move WS-STR (II:1) to WS-NUM (JJ:1)
* ---- Do nothing
when other
continue
End-Perform
Compute REAL-DEC-NUM = Function NUM-VAL (WS-NUM)

Related

Array processing and Table handling with packed decimal in COBOL

I was practicing array processing and table handling and there's this output of my program that I don't understand.
01 TABLE-VALUES.
05 TABLE-ELEMENTS OCCURS 2 TIMES.
10 A-A PIC X(5).
10 A-B PIC S9(5)V99 COMP-3.
01 WS-STRING PIC X(10).
01 S PIC 99.
01 WS-COUNT PIC 99.
...
PROCEDURE DIVISION.
0000-MAIN.
DISPLAY 'HELLO WORLD'
MOVE '1234567890ABCDEFGHI' TO TABLE-VALUES
DISPLAY 'TABLE VALUES ' TABLE-VALUES
MOVE 0 TO WS-COUNT
INSPECT TABLE-VALUES TALLYING WS-COUNT FOR CHARACTERS
DISPLAY WS-COUNT
PERFORM 1000-PARA VARYING S FROM 1 BY 1 UNTIL S > 2
STOP RUN.
1000-PARA.
MOVE 'A-A(&&) = ' TO WS-STRING
INSPECT WS-STRING REPLACING FIRST '&&' BY S
DISPLAY WS-STRING A-A(S)
MOVE 'A-B(&&) = ' TO WS-STRING
INSPECT WS-STRING REPLACING FIRST '&&' BY S
DISPLAY WS-STRING A-B(S).
The output turned out to be:
HELLO WORLD
TABLE VALUES 1234567890ABCDEFGHI
18
A-A(01) = 12345
A-B(01) = 6 7 8
A-A(02) = 0ABCD
A-B(02) = 5 6 7
I don't understand how A-B(1) and A-B(2) turned out like that. Why are there spaces in between? Where did digit 9 go to?
Try removing the COMP-3 from the A-B definition, it should work better.
Cobol comp-3
Comp-3 is cobol's binary coded decimal format. Each 4 bytes (1/2 byte or nyble) represents a decimal digit with the sign held in the last digit. Moving a character string to a comp-3 value (like you do) will result in a invalid comp-3 value.
Normally the value 1234 would be stored as
`01234C`x
In your case (if using an EBCDIC machine) you are moving 6789 hex string 'f6f7f8f9'x
to the variable A-B(01). The F's are not valid decimal digits and 9 is not a valid comp-3 sign.
Move to Table Explanation
Explanation of
MOVE '1234567890ABCDEFGHI' TO TABLE-VALUES
in the above TABLE-VALUES is treated as a pic x(18). The move completely ignores the definitions of A-A and A-B.
Assuming a definition of
01 TABLE-VALUES.
05 TABLE-ELEMENTS OCCURS 2 TIMES.
10 A-A PIC X(5).
10 A-B PIC S9(5)V99.
The following would make more sense
MOVE 'A-A 100234567A-A 200234567' TO TABLE-VALUES
When doing a move to a group level, The string has to exactly match the
map of the fields in the Group.
For the above Cobol Layout, the fields are aligned like
Field Position Length
A-A(1) 1 5
A-B(1) 6 7
A-A(2) 13 5
A-B(2) 18 7
I don’t know which compiler and directives you are using, but I would have expected the original DISPLAY of the comp-3 usage to ABEND with a numeric exception.
As for the follow up question about the letters in the numeric field, you valued the field by moving a literal to a group item, TABLE-VALUES. Group items always are USAGE ALPHANUMERIC.

COBOL supress last number while summing two decimal numbers

According to the COBOL code below when I try to sum WS-NUM1 with WS-NUM2, COBOL seems to supress the last number. For example: variable WS-NUM1 and WS-NUM2 are 10.15, I get 20.20 as result but expected 20.30. What's wrong?
WS-NUM1 PIC 9(2)V99.
WS-NUM2 PIC 9(2)V99.
WS-RESULTADO PIC 9(2)V99.
DISPLAY "Enter the first number:"
ACCEPT WS-NUM1.
DISPLAY "Enter the second number:"
ACCEPT WS-NUM2.
COMPUTE WS-RESULTADO = WS-NUM1 + WS-NUM2.
Thanks in advance.
PIC 9(2)v99 defines a variable with an implied decimal place not a real one. You're trying to enter data containing a decimal point and it's not working because you have to strip out the '.' to get the numeric part of your data to properly fit in the 4 bytes that your working storage area occupies.
PROGRAM-ID. ADD2.
data division.
working-storage section.
01 ws-num-input pic x(5).
01 WS-NUM1 PIC 9(2)V99 value 0.
01 redefines ws-num1.
05 ws-high-num pic 99.
05 ws-low-num pic 99.
01 WS-NUM2 PIC 9(2)V99 value 0.
01 redefines ws-num2.
05 ws-high-num2 pic 99.
05 ws-low-num2 pic 99.
01 WS-RESULTADO PIC 9(2)V99.
PROCEDURE DIVISION.
DISPLAY "Enter the first number:"
*
accept ws-num-input
unstring ws-num-input delimited by '.'
into ws-high-num, ws-low-num
DISPLAY "Enter the second number:"
accept ws-num-input
unstring ws-num-input delimited by '.'
into ws-high-num2, ws-low-num2
*
COMPUTE WS-RESULTADO = WS-NUM1 + WS-NUM2.
DISPLAY WS-RESULTADO
STOP RUN
.
This is just a simple demonstration. In a real world application you would have to insure much more robust edits to ensure that valid numeric data was entered.
If I declare it like this
01 WS-NUM1 PIC 9(2)V99.
01 WS-NUM2 PIC 9(2)V99.
01 WS-RESULTADO PIC 9(2)V99.
and define and sum them up like this
SET WS-NUM1 TO 10.15.
SET WS-NUM2 TO 10.15.
COMPUTE WS-RESULTADO = WS-NUM1 + WS-NUM2.
DISPLAY WS-RESULTADO.
I get the expected result of 20.30.
This looks like a job for a special type of PICture : Edited picture
Indeed you seem to know about the vanilla PICture clause (I'm writing PICture because as you may know it you can either write PIC or PICTURE).
A vanilla number PIC contains only 4 different symbols (and the parentheses and numbers in order to repeat some of the symbols)
9 : Represents a digit. You can repeat by using a number between parentheses like said before.
S : Means that the number is signed
V : Show the position of the implicit decimal point
P : I've been told that it exists but I honestly never found it in the codebase of my workplace. Its another kind of decimal point used for scaling factors but I don't know much about it.
But there are other symbols.
If you use theses other mysterious symbols the numeric PIC becomes an edited numeric PIC. As its name says, an edited PICture is made to be shown. It will allow you to format your numbers for better presentation or to receive number formatted for human reading.
Once edited, you cannot use it to make computations so you will have to transfer from edited to vanilla to perform computations on the latter. And you move from vanilla to edited in order to display your results.
So now I shall reveal some of these mysterious symbols :
B : Insert a blank at the place it is put
0 : Insert a zero at the place it is put
. : Insert the decimal point at the place it is put
: Insert a + if the number is positive and a - if the number is negative
Z : Acts like a 9 if the digits it represents has a value different than 0. Acts like a blank if the digits has the value of 0.
To my knowledge there are also : / , CR DB * $ -
You can look up for it on the internet. They really show the accountant essence of cobol.
For your problems we are really interested by the "." which will allow us to take into account the decimal point you have the write when you type down your input.
For a bonus I will also use Z which will make your result looks like 2.37 instead of 02.37 if the number is less than ten.
Note that you cannot use the repeating pattern with parenthesis ( 9(03) for instance) when describing an edited picture ! Each digits has to represented explicitly
IDENTIFICATION DIVISION.
PROGRAM-ID. EDITCOMP.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-NUM1-EDITED PIC 99.99.
01 WS-NUM2-EDITED PIC 99.99.
01 WS-NUM1-CALC PIC 9(2)V99.
01 WS-NUM2-CALC PIC 9(2)V99.
01 WS-RESULTADO-EDITED PIC Z9.99.
PROCEDURE DIVISION.
ACCEPT WS-NUM1-EDITED.
ACCEPT WS-NUM2-EDITED.
MOVE WS-NUM1-EDITED TO WS-NUM1-CALC.
MOVE WS-NUM2-EDITED TO WS-NUM2-CALC.
COMPUTE WS-RESULTADO-EDITED = WS-NUM1-CALC + WS-NUM2-CALC.
DISPLAY WS-RESULTADO-EDITED.
STOP RUN.
You should note that there also exist edited alphanumeric picture. You can insert Blank (B), zeroes (0) or / (/) in it.

How used DIVIDE with a CONDENSED NUMERIC Varibale in COBOL

My problem is: I have data in a database table. The column is: Z-ZYTL-RTPDHR defined as NOT NULL NUMBER(5,2) .
So I have a program that I need to move my data in one variable "H-ZYTL-RTPDHR" and after I will move this value divide by 100 in one column Z8 when i compile i obtained Excel with data in the different column.
My data:
Z8
------
34,28
70
97
8,57
21,43
94,28
94,28
100
40
40
what I should get:
Z8
-------
0,3428
0,7
0,97
0,0857
0,2143
0,9428
0,9428
0,100
0,40
0,40
my question how to declare the variable to obtain the good result?
Variables that I declared:
01 FILLER.
05 H-ZYTL-RTPDHR.
10 PIC S9(5)V9(2) comp-3 VALUE.
05 FILLER REDEFINES H-ZYTL-RTPDHR.
10 H-ZYTL-RTPDHR comp-3 pic s9(5)v99.
Equivalent Temps Plein
05 W-Z8 PIC -(5),99.
05 FILLER PIC X(001) VALUE ';'.
ALIM-WZ8 SECTION.
IF Z-ZYTL-NOMBRE > ZERO
IF TLCODTRA(Z-ZYTL-NOMBRE) NOT = SPACES
MOVE Z-ZYTL-RTPDHR(Z-ZYTL-NOMBRE) TO
H-ZYTL-RTPDHR
DISPLAY 'H-ZYTL-RTPDHR:' H-ZYTL-RTPDHR
DIVIDE H-ZYTL-RTPDHR BY 100 GIVING W-Z8
DISPLAY 'W-Z8 : ' W-Z8
END-IF
END-IF.
You have defined the name H-ZYTL-RTPDHR twice: that will confuse you and and the compiler as well.
Your second definition of H-ZYTL-RTPDHR is good for doing arithmetic. All you need is
01 FILLER.
10 H-ZYTL-RTPDHR comp-3 pic s9(5)v99.
Your first definition of H-ZYTL-RTPDHR defines a group level item, and you can't do arithmetic with it.
However, your question doesn't show the definition of Z-ZYTL-RTPDHR. If it's not numeric, then you won't get the results you expect.

COBOL- Add number of characters to a string based on variable

How would I add characters to the beginning of a string based on a variable? For example a vendor we use for telephone numbers converts out string to a numeric which drops the leading 0's off of the phone number. When they send us the report back we convert the number back into a string but now it doesn't have the correct amount of numbers. I'm trying the following:
IF LENGTH(TO-NUM) < 10
SUBTRACT LENGTH(TO-NUM) FROM 10 GIVING ADD-NUM-ZERO
Now I need to figure out how to add ADD-NUM-ZERO number of 0's to the beginning of the string TO-NUM without overwriting the characters already at the beginning.
Assuming that TO-NUM contains only digits followed by spaces (or spaces followed by digits), then
1 TO-NUM PIC X(10).
1 temp-x.
5 temp-9 pic 9(10).
if to-num not numeric
compute temp-9 = function numval (to-num)
move temp-x to to-num
end-if
will, if necessary, replace the previous content with the same value but with leading zeros.
The IF TO-NUM NOT NUMERIC statement is equivalent to asking if the number of digits in TO-NUM is less than 10.
For example, if to-num was '5551212 ' before, then to-num will be '0005551212' after.
If to-num contains non-digits, then it would be necessary to extract the digits by parsing to-num into temp-9.
Working-storage section.
01 NUM PIC 9(10) COMP-5.
01 TELNUM-G.
03 TELNUM PIC 9(10).
03 TELNUM-S REDEFINES TELNUM.
05 AREACODE PIC 999.
05 THREEDIGIT PIC 999.
05 FOURDIGIT PIC 9999.
01 TELOUTPUT PIC X(13) VALUE '(AAA)TTT-NNNN'.
Procedure division.
Move 31234 to NUM.
MOVE NUM TO TELNUM.
INSPECT TELOUTPUT
REPLACING ALL 'AAA' BY AREACODE
ALL 'TTT' BY THREEDIGIT
ALL 'NNNN' BY FOURDIGIT
This code assume the number is in "NUM", and it can be in any of ZONE/PACK/BINARY/COMP-3/COMP-5 formats. I've used COMP-5 in this example code.

Problem with COBOL move to comp-3 variable

I'm having the following problem in a COBOL program running on OpenVMS.
I have the following variable declaration:
01 STRUCT-1.
02 FIELD-A PIC S9(6) COMP-3.
02 FIELD-B PIC S9(8) COMP-3.
01 STRUCT-2.
03 SUB-STRUCT-1.
05 FIELD-A PIC 9(2).
05 FIELD-B PIC 9(4).
03 SUB-STRUCT-2.
05 FIELD-A PIC 9(4).
05 FIELD-B PIC 9(2).
05 FIELD-C PIC 9(2).
And the following code:
* 1st Test:
MOVE 112011 TO FIELD-A OF STRUCT-1
MOVE 20100113 TO FIELD-B OF STRUCT-1
DISPLAY "FIELD-A : " FIELD-A OF STRUCT-1 CONVERSION
DISPLAY "FIELD-B : " FIELD-B OF STRUCT-1 CONVERSION
* 2nd Test:
MOVE 112011 TO SUB-STRUCT-1.
MOVE 20100113 TO SUB-STRUCT-2.
MOVE SUB-STRUCT-1 TO FIELD-A OF STRUCT-1
MOVE SUB-STRUCT-2 TO FIELD-B OF STRUCT-1
DISPLAY "SUB-STRUCT-1 : " SUB-STRUCT-1
DISPLAY "SUB-STRUCT-2 : " SUB-STRUCT-2
DISPLAY "FIELD-A : " FIELD-A OF STRUCT-1 CONVERSION
DISPLAY "FIELD-B : " FIELD-B OF STRUCT-1 CONVERSION
Which outputs:
FIELD-A : 112011
FIELD-B : 20100113
SUB-STRUCT-1 : 112011
SUB-STRUCT-2 : 20100113
FIELD-A : 131323
FIELD-B : 23031303
Why FIELD-A and FIELD-B hold values different from what I move into them in the second test?
I've other moves from DISPLAY to COMP-3 variables in my program where I don't get this behavior.
In COBOL, Group level data are typeless and are moved without casting.
Element level data always have an associated data type. Typed
data are cast to match the type
of the receiving element during a MOVE.
In the first instance you MOVE a literal numeric value (112011) to a packed decimal field and the compiler converts it to the correct data type in the process. Just as you would expect in any programming language.
In the second instance you MOVE a literal value to a group item. Since this is a group item the compiler cannot 'know' the intended data type so it always does group moves as character data (no numeric conversions). This is fine when the receiving item has a PICTURE clause that is compatible with character data - which FIELD-A and FIELD-B
of SUB-STRUCT-1 are. There is no difference in the internal representation of a 9 when stored as PIC X and when stored as PIC 9. Both are assumed USAGE DISPLAY.
Now when you do a group level move from SUB-STRUCT-1 to a COMP-3 (Packed Decimal) you effectively tell the compiler not to convert from DISPLAY to COMP-3 format. And that is what you get.
Try the following modifications to your code. Using REDEFINES creates
a numeric elementary item for the move. COBOL will do the appropriate
data conversions when moving elementary data.
01 STRUCT-2.
03 SUB-STRUCT-1.
05 FIELD-A PIC 9(2).
05 FIELD-B PIC 9(4).
03 SUB-STRUCT-1N REDEFINES
SUB-STRUCT-1 PIC 9(6).
03 SUB-STRUCT-2.
05 FIELD-A PIC 9(4).
05 FIELD-B PIC 9(2).
05 FIELD-C PIC 9(2).
03 SUB-STRUCT-2N REDEFINES
SUB-STRUCT-2 PIC 9(8).
And the following code:
* 3RD TEST:
MOVE 112011 TO SUB-STRUCT-1.
MOVE 20100113 TO SUB-STRUCT-2.
MOVE SUB-STRUCT-1N TO FIELD-A OF STRUCT-1
MOVE SUB-STRUCT-2N TO FIELD-B OF STRUCT-1
DISPLAY "SUB-STRUCT-1 : " SUB-STRUCT-1
DISPLAY "SUB-STRUCT-2 : " SUB-STRUCT-2
DISPLAY "FIELD-A : " FIELD-A OF STRUCT-1
DISPLAY "FIELD-B : " FIELD-B OF STRUCT-1
Beware: Moving character data into a COMP-3 field may give you the dreaded SOC7 data exception abend when the receiving item is referenced. This is because not all bit patterns are valid COMP-3 numbers.
You have 2 Issues.
COBOL has several Numeric Data Structures. Each has its own set of rules.
For PACKED DECIMAL ( COMP-3 )
• The numeric components of the PIC clause should ALWAYS add up to an ODD number.
• The decimal marker “V” determines the placement of the decimal point.
• The individual element MOVE and math functions will maintain the decimal value alignment – both high and low level truncation is possible
• Numeric data type conversion (zone decimal to packed & binary to packed) is handled for you.
e.g. S9(5)V9(2) COMP-3.
including the 2 decimal positions>
Length is calculated as ROUND UP[ (7 + 1) / 2] = 4 bytes
S9(6)V9(2) COMP-3.
including the 2 decimal positions >
Length is calculated as ROUND UP[(8 + 1) / 2] = 5 bytes
But the 1st ½ byte is un-addressable
The last ½ byte of the COMP-3 fields is the HEXIDECIMAL representation of the sign.
The sign ½ byte values are C = signed positive D = signed negative F = unsigned (non COBOL).
S9(6)V9(3) COMP-3 VALUE 123.45.
Length is calculated as ROUND UP[(9 + 1) / 2] = 5 bytes
Contains X’00 01 23 45 0C’
Note the decimal alignment & zero padding.
Group Level MOVE rules
COBOL Data field structures are define as hierarchical structures.
The 01 H-L group field – & any sub-group level field –
Is almost always an implied CHARACTER string value
If an individual element field is a 01 or 77 level – then it can be numeric.
Individual element fields defined as a numeric under a group or sub-group level will be treated as numeric if referenced as an Individual element field.
Numeric rules apply.
o Right justify
o decimal place alignment
o pad H-L (½ bytes) with zeros
o Numeric data type conversion
The receiving field of a MOVE or math calculation determines if a numeric data conversion will occur.
Numeric Data Conversion
If you MOVE or perform a math calculation using any sending field type (group or element) to any receiving individual element field defined using a numeric PIC clause --- then numeric data conversion will occur for the receiving field. S0C7 abends occur when non-numeric data is MOVE ‘d to a receiving numerically defined field OR when math calculations are attempted using non-numeric data.
No Numeric Data Conversion
If you move any field type (group or element) to any group or sub-group level field then there will be NO numeric data conversion.
• Character MOVE rules apply.
• Left Justify & pad with spaces.
This is one of the primary causes of non-numeric data in a numerically defined field.
One of the primary uses of a sending group level MOVE containing numeric element fields to a receiving group level containing numeric element fields (mapped identically) is for re-initializing numeric element fields using 1 MOVE instruction.
A Clear Mask – or – a data propagation MOVE is also possible for table clears - where the table group level is greater than 255 bytes.

Resources