CONVERT COBOL TEXT STRING TO NUMBER - parsing

I have a project that I'm working on where I need to take a substring that is given to me and convert it to a number.
One of the common values I have is 000300000. This translates to 3000.00. I would LOVE to be able to figure out a way to have it display as "$3,000.00". Can someone show me a way that I can do this?
The PARSING field looks like this:
SUBSTRING(Text,521, 9)
I've tried this: select STUFF(CAST(SUBSTRING(Text,521, 9) AS INT), LEN(CAST(SUBSTRING(Text,521, 9) AS INT), 'N0')-1, 0, '.') but am getting an argument error.
Any help is appreciated.

Assuming you are doing this in COBOL!
01 CONVERSION.
05 NUMBER-AS-STRING PIC X(9).
05 NUMBER-AS-NUMBER REDEFINES NUMBER-AS-STRING PIC 9(7)V99.
05 NUMBER_-TO-PRINT PIC $$$$$$9.99.
........
MOVE '000300000' to NUMBER-AS-STRING.
MOVE NUMBER-AS-NUMBER TO NUMBER-TO-PRINT.
DISPLAY NUMBER-TO-PRINT

Related

Cobol - Designating where a file expansion takes place in a field

I am expanding a field S9(8)V9(3) to S9(8)V9(4). The problem is that i do not want the output data to display like this:
12345678.123 -> 12345678.0123
I would like it to look like this:
12345678.1203.
How could i do this?
I have not tried much as I am not sure where to begin.
Expanding the picture to add another place after the decimal point would not cause it to display as 12345678.0123; rather, it would display as 12345678.1230.
How could i do this?
Use MOVE statements to separate in parts, then combine the parts. This is accomplished by using automatic truncation to the PICTURE clause.
The MOVE to part-1 strips the trailing digit. The MOVE to part-2, by using the P place holder, strips all except the last digit. After, the COMPUTE statement combines the parts as needed.
Code:
data division.
working-storage section.
01 in-num pic s9(8)v9(3) value +12345678.123.
01 part-1 pic s9(8)v99 value +0.
01 part-2 pic svpp9 value +0.
01 out-num pic 9(8).9(4).
01 in-disp pic 9(8).9(3).
procedure division.
move in-num to part-1 part-2 in-disp
compute out-num = part-1 + (part-2 / 10)
display in-disp " -> " out-num
goback
.
I would like it to look like this: 12345678.1203.
12345678.123 -> 12345678.1203

Abend S0C7 error while moving data to COMP-3 fields

Moving data to COMP-3 fields after UNSTRING.
UNSTRING is working fine but I am not able to move data to COMP-3 fields without an S0C7 data exception abend.
I think it is an issue with storing data.
Below is my COBOL program.
IDENTIFICATION DIVISION.
PROGRAM-ID. ADDPROG.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 VALUEA PIC X(20) VALUE '64.99|64.99'.
01 NOA PIC S9(9)V9(02).
01 NOB PIC S9(9)V9(02).
01 NOC PIC S9(9)V99 COMP-3.
01 NOD PIC S9(9)V99 COMP-3.
PROCEDURE DIVISION.
000-MAIN.
DISPLAY "EARLIER".
DISPLAY 'NOA-' NOA.
DISPLAY 'NOB-' NOB.
DISPLAY "AFTER".
UNSTRING VALUEA
DELIMITED BY '|'
INTO NOA,NOB.
DISPLAY 'NOA-' NOA.
DISPLAY 'NOB-' NOB.
MOVE NOA TO NOC.
MOVE NOB TO NOD.
DISPLAY 'NOC-' NOC.
DISPLAY 'NOD-' NOD.
STOP RUN.
Output I am getting after compiling.
Please let me know is there any other way to move data to COMP-3 fields or to avoid this error.
Your code has two main problems. To see them you first have to understand that your UNSTRING into NOA and NOB works like any other character-to-character MOVE.
So it starts from the left and moves character after character until one field ends and if necessary adds some blanks to fill up the receiving field.
So problem one is that NOA contains the value left-justified while according to your PIC-clause it should be right-justified, so you would need an intermediate PIC X(12) JUSTIFIED RIGHT field that you UNSTRING to.
The second problem that is causing the S0C7 is that your PIC-clause does not include a decimal-point. The V specifies the implied position of the decimal point but it would not show on output nor is it handled correctly when parsing the field-contents. To have a field that correctly handles the decimal point you should have a PIC S9(9)V.9(02).
Please also see this question.
Like piet.t said, the UNSTRING statement is like a alphanumeric move. Therefore my suggestion is to:
make NOA and NOB be alphanumeric (PIC X(12)), and
change your move statements to include WITH CONVERSION so that it will convert the numeric data in the alphanumeric field into COMP-3.
Your decimal data field are wrong just have . Like 01 NOA PIC S9(9).(02) In them! Same with noc variabile then it should run.
To move data from a numeric field to a COMP-3 field, you need to make sure that the data in the numeric field is valid for the COMP-3 field. A COMP-3 field stores packed decimal data, which means that the digits of the number are packed into two-digit decimal fields. The decimal point is not stored, and the sign of the number is stored in the last nibble of the field.

INITIALIZE gives garbage value on PIC X and PIC S9 COMP variables

When initializing the following variables:
01 BATCH-REC.
03 BATCH-VERSION PIC X(2).
03 BATCH-FIELDS PIC X(682).
03 BATCH-REC-01 REDEFINES BATCH-FIELDS.
05 B01-OH-DTL-REC.
07 B01-PE-ID PIC X(12).
07 B01-PMT-DISC-TERMS PIC S9(4) COMP.
07 B01-PMT-DISC-AMT PIC S9(18) COMP.
using the command
INITIALIZE BATCH-REC.
the variables B01-PMT-DISC-TERMS is initialized to value +08224 and B01-PMT-DISC-AMT is initialized to +314885530818453536.
What could the reason be? Would it be a good idea to MOVE a blank space to those variables after initializing? I dont want to change that BATCH-REC code to add a default value on them.
The INITIALIZE statement will not initialize redefined fields. If you want the redefined fields to be initialized, you must identify them in some way. For example,
INITIALIZE BATCH-VERSION BATCH-REC-01
This will initialize the elementary fields to SPACES or ZEROS depending on their PICTURE. BATCH-FIELDS will not be initialized.
Item 3, below applies, in this case. From the 2002 standard for INITIALIZE:
5) The receiving-operand in each implicit MOVE or SET statement is determined by applying the following steps in order:
a) First, the following data items are excluded as receiving-operands:
Any identifiers that are not valid receiving operands of a MOVE statement, except data items of category data-pointer, object-reference, or program-pointer.
If the FILLER phrase is not specified, elementary data items with an explicit or implicit FILLER clause.
Any elementary data item subordinate to identifier-1 whose data description entry contains a REDEFINES or RENAMES clause or is subordinate to a data item whose data description entry contains a REDEFINES clause. However, identifier-1 may itself have a REDEFINES clause or be subordinate to a data item with a REDEFINES clause.
In this case I think you will find your INITIALIZE statement did initialize your PIC X data to spaces. The value you have for B01-PMT-DISC-TERMS is x'2020' and the value for B01-PMT-DISC-AMT is x'2020202020202020' with the leading digit cut off, possibly due to reporting of the value being limited to the picture clause of 18 digits.
Regardless, I agree with #RickSmith and believe he is correct in his solution to your problem.

Compare BINARY INTEGER variable with ALPHANUMERIC variable in COBOL

IF AWA-REQ-DATE < WS-JULIAN-DATE
MOVE VAR1 to VAR2
The AWA-REQ-DATE is a binary integer i.e. PIC S9(09) COMP, whereas Julian date is PIC X(10) VALUE SPACES.
Both have Julian date inside them like 2013031 & 2013099.
This gives:
ERROR:REQ-DATE (BINARY INTEGER)" was compared with "WS-JULIAN-date (ALPHANUMERIC)". Discarded
Can I compare then with converting one of them to other format right here in code?
All your four-digit-year Julian dates will contain seven digits. They are dates, so naturally are positive.
It is unclear why you have a nine-digit, signed, binary to hold such or date. Nor a 10-byte alphanumeric. It is also unclear why this should have an initial value of SPACE.
01 WS-JULIAN-DATE VALUE SPACE.
05 WS-JULIAN-DATE-NUM PIC 9(7).
05 FILLER PIC XXX.
This assumes all your WS-JULIAN-DATE values are left-aligned.
IF AWA-REQ-DATE < WS-JULIAN-DATE
MOVE VAR1 to VAR2
END-IF
Hopefully VAR1 and VAR2 are just sample names for the question. If not, please make them meaningful, as it will make it much easier for the next person reading the program to understand. And that might be you.
If the values of WS-... are not guaranteed to be NUMERIC, test them for NUMERIC and take appropriate action (according to your spec) if they are not.
The nine-digit binary will potentially generate extra code beyond what is needed.
Another possibility is:
01 WS-AWA-REQ-JULIAN-DATE VALUE SPACE.
05 WS-AWA-REQ-JULIAN-DATE-NUM PIC 9(7).
05 FILLER PIC XXX.
MOVE AWA-REQ-DATE TO WS-AWA-REQ-JULIAN-DATE-NUM
IF WS-AWA-REQ-JULIAN DATE < WS-JULIAN-DATE
MOVE VAR1 to VAR2
END-IF
Which you choose can depend on what else you are doing with the fields.
Also, if one is "invariant", convert that to the same format as the variable one, once only.
In the various ISO/ANSI COBOL standards, comparing alphanumeric (PIC X) and numeric (PIC 9) is like comparing apples and oranges. There are defined rules. Of course, each compiler has different ways of interpreting the standard. Therefore, you are best off converting one of the fields to the other format and comparing those. Bill Woodger has some good comments about which field to convert, and how.
In summary, you should always compare like data types. For numeric items, it's best if you can compare the same COMP format, but sometimes this can't be done. If that is the case, you need to read your compiler documentation to see how comparisons are performed between different computational types, and any gotchas (such as COMP-4 and COMP-5 in IBM's Enterprise COBOL).
The answer probably depends on which compiler you are using. On GNU Cobol, the comparison works after applying FUNCTION NUMVAL to the Julian (text) date.
As the error message is describing: a numeric value PIC S9(9) COMP cannot be compared to a string PIC X(10).
A COMP variable is a system dependent, numeric representation of the value. There are variations to the internal representation (binary, BCD,...) used for COMPutation. The value of your date is,depending on the machine though, internally represented as: 0011110 10110111 01100111
Display variables are storing each position of your variable according to an encoding standard (ASCII, EBCDIC, UNICODE). Again depending on your machine and the encoding table, the representation of your variable might be: 00110010 00110000 00110001 00110011 00110000 00111001 00111001
I do some guessing here in order to address the underlying problem
First guess: The alpha definition PIC X(10) looks like the date is in printing format and should contain separators in order to make it 10 characters long
21.02.2014
123456789A
Such a variable shouldn't hold a julian date after all and you probably need a date comparing function.
Second guess: for some displaying reason, you want to have a variable of 10 characters long, holding the julian date in the first 7 characters. In this case, I'd define a structure and you can keep your comparison as is:
01 WS-JULIAN-DATE-PRINT VALUE SPACE.
05 WS-JULIAN-DATE PIC 9(7).
05 FILLER PIC X(3).

How to display the actual value of a comp variable in cobol

I have the following variable in COBOL program which gets its value from a file, when it is read:
01 Employee-number PIC 09(8) comp
01 Employee-number-x redefines
Employee-number PIC x(04)
I have another variable in the same program:
01 D-element-number PIC 9(04)
Now,
Move Employee-number to D-element-number
Then I write this D-element-number to a file
value read from input file is :
0013
0024
so this value comes to Employee-number and Employee-number-x and I move this value to D-Element-number and write this variable to a output file.
But I get this in the output file:
4660
FFFF
4660
4660 is the decimal equivalent of X'1234'
But I want to see something like:
1234
FFFF
1234
How can I achieve this ?
I am allowed to change the definition of D-element-number but nothing else.
Assuming that when you have X'00001234' you want C'00001234', have a look here. http://ibmmainframes.com/viewtopic.php?p=234231#234231
Ignore the rest of the discussion for now, just concentrate on that post.
This is the key part:
PERFORM UNTIL X-WS-1000 > X-WS-1000-MAX
MOVE WS-1000-BYTE-TBL (X-WS-1000)
TO WS-PACKED-X (1:1)
MOVE WS-PACKED TO WS-DISPLAY
MOVE WS-DISPLAY-X TO WS-2000-BYTE-TBL (X-WS-2000)
SET X-WS-1000 UP BY 1
SET X-WS-2000 UP BY 1
END-PERFORM
You need the exact storage definitions to go with it (don't "correct" anything).
It works by getting the compiler to use the "unpack" instruction (UNPK). This one is working one byte at a time, so simple to explain.
X'12' (an example on-byte field) is put in a two-byte field. X'12ii' (where the value of ii is "irrelevant").
UNPK will then turn this "packed" number into a "zoned" number. A "Zone" is the first four bts of a byte, and for a Zoned number, the four bits are all set to one. So you get an F. Then you get the left-most digit from the first byte. Then you get the second output byte, first four bits set to F, then the second input digit.
Then the UNPK continues with the final byte, which contains one irrelevant digit, and an irrelevant sign. For a Zoned number, the sign and right-most digit occupy the same byte (the sign in the zone) so you get a whole byte of irrelevance.
X'12' -> X'12ii' -> X'F1F1ii'.
The first two bytes of the three-byte output are C'12'.
Nos, what is all fine for numbers, but letters make a mess:
X'AB' -> X'ABii' -> X'FAFBii'
Although F and a digits gives a displayable number, for F and a letter, the result does not mean much directly.
Now the INSPECT ... CONVERTING comes to the rescue: FA gets translated to C'A' (X'C1), and the same for the letters through to F.
Your results after the CONVERTING will be C'AB'.
Should give you enough to work on.
There are other methods, but this is a fair COBOL approximation to the classic Assembler technique with UNPK and a TRANSLATE (TR) and a table of translation values.
If you use your favourite search engine. you should be able to fine more methods, using calculation (more than one), table-lookups, I've even seen a 256-WHEN EVALUATE that "works", but I guess it is a little on the "slow" side.
Thinking further, you actually have a BCD (Binary Coded Decimal) don't you? This is a Packed Decimal, without the sign. You don't have any alphas in your field.
This is even simpler to convert.
01 the-converted-value PACKED-DECIMAL PIC 9(8)V9 VALUE ZERO.
01 FILLER REDEFINES the-converted-value.
05 the-binary-value PIC X(4).
05 FILLER PIC X.
MOVE Employee-number-x TO the-binary-value
MOVE the-converted-value TO D-element-number (with Gilbert's correction to PIC 9(8)).
The "decimal place" is the ii, the ignore value and ignore sign. You no longer need the INSPECT ... CONVERTING ... as you only have numeric digits. If you have a BCD...
A really good way for you to have answered your own question would have been to find out how the number was created in the binary field in the first place.
Didn't try this, but I think it might work:
01 WN-GROUP.
05 Employee-number PIC 9(8) comp.
05 PACKED-ZERO PIC 9(1) COMP-3 VALUE ZERO.
01 WN-PACKED redefines WN-GROUP PIC 9(9) COMP-3.
01 WN-UNPACKED PIC 9(9).
Then in your procedure:
MOVE your-number TO Employee-number
MOVE WN-PACKED TO WN-UNPACKED.
And finally you can move or display WN-PACKED(1:8).

Resources