Using a constant does work: MOVE X "41" TO HEXSTRING-WRK.
Value of HEXSTRING-WRK = 'A
But the hex-value "41" should be variable, how can this be realized with MOVE?
My attempts without success:
01 TEST-VAR PIC X(28) VALUE "41".
01 HEXSTRING-WRK PIC X(28).
01 HEXSTRING-WRK2 PIC X(30).
STRING '"X"' DELIMITED BY SIZE
TEST-VAR DELIMITED BY SIZE
INTO HEXSTRING-WRK2
END STRING.
MOVE HEXSTRING-WRK2 TO HEXSTRING-WRK.
Hex value of HEXSTRING -> 2258223431
IBM COBOL on the AS400
Related
I have Alphanumeric value = '86' and its length is defined as PIC x(02). I need to convert it into hex x'86' and its length is defined as PIC 9(01) comp-3.
example:
01 WS-ALPHANUMERIC PIC X(02) VALUE '86'.
01 WS-HEX PIC 9(01) COMP-3.
PROCEDURE DIVISION.
MOVE WS-ALPHANUMERIC TO WS-HEX.
DISPLAY WS-HEX.
STOP RUN
I am getting x'FF' in my spool. But I am expecting x'86'.
Why your code doesn't produce the output you're expecting
It is just guessing from my part for on my computer it doesn't work that way.
When you MOVE from WS-ALPHANUMERIC to WS-HEX, the string '86' in transformed in the decimal number 86.
However WS-HEX is only one byte long and in the COMP-3 format. This format can only store one decimal digit and the sign.
I'm guessing that on your environment when you move a bigger number than the capacity to a COMP-3 it take the biggest hexadecimal value it can hold : 0xF.
In my environment it would just take the digit 6 of the number 86.
So when you display, it is converted to a usage display so you have your firt 0xF for the usage formatting and then your 0xF for the "overflow" I guess.
On my computer you would just get a 0xF6.
A solution to produce the expected output
Disclaimer : I originally thought that your input would only be decimals, like '87596', '12' or '88'. This solution does not work for hexadecimals input like 'F1' ou '99F'. I built more complete solutions below in items 3 and 4 by improving this one
The solution I propose can take up to 16 digits in the input string if your system is 64bit because it takes 4 bits to store a hexadecimal digit.
Therefore if you want a larger input you'll have to use more than one result variable.
If you want to have it in only one byte, you just have to make Result a PIC 9(1) instead of PIC 9(18)
IDENTIFICATION DIVISION.
PROGRAM-ID. CNVRSN.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 RawInput PIC X(02) VALUE '86'.
01 FormattedInput PIC 9(16).
01 FractionedInput REDEFINES FormattedInput
05 Digit PIC 9 OCCURS 16.
01 Shifting PIC 9(18) COMP-5 VALUE 1.
01 I PIC 99 COMP-5.
01 Result PIC 9(18) COMP-5 VALUE 0.
01 DisplayResult REDEFINES Result PIC X(8).
PROCEDURE DIVISION.
MOVE RawInput TO FormattedInput.
PERFORM VARYING I FROM LENGTH OF FractionedInput
BY -1 UNTIL I < 1
COMPUTE Result = Result + Digit(I)*Shifting
MULTIPLY 16 BY Shifting
END-PERFORM
DISPLAY 'DisplayResult : ' DisplayResult
.
END PROGRAM CNVRSN.
The code works by transforming the string in a number of USAGE DISPLAY with the first move MOVE RawInput to FormattedInput.
We use the fact that each digit has the same format as a number of just one digit (PIC 9). This allows us to split the number in elements of an array with the REDEFINES of FomattedInput inFractionedInput
As you can see I traverse the array from the end to start because the least significant byte is at the end of the array (highest address in memory), not at the start (lowest address in memory).
Then we place each the hexadecimal digit in the correct place by shifting them to the left by 2^4 (a nibble, which is the size of a hexadecimal digit) as many times as required.
A solution that accepts the full hexadecimal input range (memory intensive)
Here is the code :
IDENTIFICATION DIVISION.
PROGRAM-ID. CNVRSN.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 RawInput PIC X(02) VALUE '86'.
01 FormattedInput PIC X(16).
01 FractionedInput REDEFINES FormattedInput
05 Digit PIC X OCCURS 16.
01 I PIC 99 COMP-5.
01 ConversionTableInitializer.
05 FILLER PIC X(192).
05 TenToFifteen PIC X(06) VALUE X'0A0B0C0D0E0F'.
05 FILLER PIC X(41).
05 ZeroToNine PIC X(10) VALUE X'00010203040506070809'.
01 ConversionTable Redefines ConversionTableInitializer.
05 DigitConverter PIC 99 COMP-5 OCCURS 249.
01 Result PIC 9(18) COMP-5 VALUE 0.
01 DisplayResult REDEFINES Result PIC X(8).
PROCEDURE DIVISION.
MOVE RawInput TO FormattedInput.
PERFORM VARYING I FROM 1 BY 1
UNTIL I > LENGTH OF FractionedInput
OR Digit(I) = SPACE
COMPUTE Result = Result*16 + DigitConverter(Digit(I))
END-PERFORM
DISPLAY 'DisplayResult : ' DisplayResult
.
END PROGRAM CNVRSN.
The idea in this solution is to convert each character (0,1...,E,F) to its value in hexadecimal. For this we use the value of their encoding as a string (0xC1 = 0d193 for A for instance) as the index of an array.
This is very wasteful of memory for we allocate 249 bytes to store only 16 nibbles of information. However to access the element of an array is a very fast operation: We are trading the memory usage for cpu efficiency.
The underlying idea in this solution is a hashtable. This solution is nothing but a very primitive hash table where the hash function is the identity function (A very, very bad hash function).
Another solution that accepts the full hexadecimal input range (CPU intensive)
Disclaimer : This solution was proposed by #Jim Castro in the comments.
IDENTIFICATION DIVISION.
PROGRAM-ID. CNVRSN.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 RawInput PIC X(02) VALUE '86'.
01 FormattedInput PIC X(16).
01 FractionedInput REDEFINES FormattedInput
05 Digit PIC 9 OCCURS 16.
01 ConversionString PIC X(16) VALUE '0123456789ABCDEF'.
01 ConversionTable REDEFINES ConversionString.
05 ConversionEntry OCCURS 16 INDEXED BY Idx.
10 HexDigit PIC X.
01 I PIC 99 COMP-5.
01 Result PIC 9(18) COMP-5 VALUE 0.
01 DisplayResult REDEFINES Result PIC X(8).
PROCEDURE DIVISION.
MOVE RawInput TO FormattedInput.
PERFORM VARYING I FROM 1 BY 1
UNTIL I > LENGTH OF FractionedInput
OR Digit(I) = SPACE
SET Idx To 1
SEARCH ConversionEntry
WHEN HexDigit(Idx) = Digit(I)
COMPUTE Result = Result*16 + Idx - 1
END-SEARCH
END-PERFORM
DISPLAY 'DisplayResult : ' DisplayResult
.
END PROGRAM CNVRSN.
Here the idea is still to convert the string digit to its value. However instead of trading off memory efficiency for cpu efficiency we are doing the converse.
We have a ConversionTable where each character string is located at the index that convey the value they are supposed to convey + 1 (because in COBOL arrays are 0 based). We juste have to find the matching character and then the index of the matching character is equal to the value in hexadecimal.
Conclusion
There are several ways to do what you want. The fundamental idea is to :
Implement a way to convert a character to its hexadecimal value
Traverse all the characters of the input string and use their position to give them the correct weight.
Your solution will always be a trade off between memory efficiency and time efficiency. Sometimes you want to preserve your memory, sometimes you want the execution to be real fast. Sometimes you wand to find a middle ground.
To go in this direction we could improve the solution of the item 3 in terms of memory at the expense of the cpu. This would be a compromise between item 3 and 4.
To do it we could use a modulo operation to restrict the number of possibilities to store. Going this way would mean implementing a real hashtable.
I don't have access to an IBM mainframe to test this code.
When I run the code on an online GnuCOBOL v2.2 compiler, I'm stuck with ASCII instead of EBCDIC.
I've posted the code. Here's what you have to do.
Make sure the top byte comes out to 8 and the bottom byte comes out to 6. You're converting the EBCDIC values to intager values. Values A - F hex will have different EBCDIC values than values 0 - 9.
Make sure the multiply and add are correct
Here's the code. You'll have to fix it to work with EBCDIC.
IDENTIFICATION DIVISION.
PROGRAM-ID. CONVERSION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ALPHANUMERIC PIC X(02) VALUE '86'.
01 WS-WORK-FIELDS.
05 WS-INPUT.
10 WS-ONE.
15 WS-TOP-BYTE PIC 99 COMP.
10 WS-TWO.
15 WS-BOTTOM-BYTE PIC 99 COMP.
05 WS-ACCUMULATOR PIC S9(4) COMP.
05 FILLER REDEFINES WS-ACCUMULATOR.
10 FILLER PIC X.
10 WS-HEX PIC X.
PROCEDURE DIVISION.
0000-BEGIN.
MOVE WS-ALPHANUMERIC TO WS-INPUT
DISPLAY WS-INPUT
COMPUTE WS-TOP-BYTE = WS-TOP-BYTE - 183
COMPUTE WS-BOTTOM-BYTE = WS-BOTTOM-BYTE - 183
IF WS-TOP-BYTE NOT LESS THAN 16
COMPUTE WS-TOP-BYTE = WS-TOP-BYTE - 57
END-IF
IF WS-BOTTOM-BYTE NOT LESS THAN 16
COMPUTE WS-BOTTOM-BYTE = WS-BOTTOM-BYTE - 57
END-IF
DISPLAY WS-TOP-BYTE
DISPLAY WS-BOTTOM-BYTE
MOVE WS-TOP-BYTE TO WS-ACCUMULATOR
MULTIPLY 16 BY WS-ACUMULATOR
ADD WS-BOTTOM-BYTE TO WS-ACCUMULATOR
DISPLAY WS-ACCUMULATOR
DISPLAY WS-HEX
GOBACK.
I am trying to define a file in File section having record length of 300 and record format VB, but I am getting error as "First integer of record contains clause for file was different from minimum"
FD INPUT-FILE
E THE FIRST INTEGER OF THE "RECORD CONTAINS" CLAUSE FOR FILE "INPUT-F //Error msg
ILE" WAS DIFFERENT FROM THE MINIMUM //Error msg
RECORDING MODE IS V
LABEL RECORDS ARE STANDARD
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 30 TO 300 CHARACTERS
DATA RECORD IS IN-02-RECORD.
01 IN-02-RECORD.
05 IN-ALL PIC X(30).
05 IN-END PIC X(270).
Your definition has only one record which is fixed length which does not match to the variable-length FD.
You may want to use multiple fixed ones and/or a variable-length one:
single variable-length:
FD INPUT-FILE
RECORDING MODE IS V
LABEL RECORDS ARE STANDARD
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 30 TO 300 CHARACTERS
DATA RECORD IS IN-02-RECORD.
01 IN-02-RECORD.
05 IN-ALL PIC X(30).
05 IN-END PIC X OCCURS 0 TO 270
DEPENDING ON some-var.
variable-length file in three possible lengths (removing the normally obsolete DATA RECORD clause):
FD INPUT-FILE
RECORDING MODE IS V
LABEL RECORDS ARE STANDARD
BLOCK CONTAINS 0 RECORDS
RECORD CONTAINS 30 TO 300 CHARACTERS
DATA RECORD IS IN-02-RECORD.
01 IN-02-RECORD.
05 IN-ALL PIC X(30).
01 IN-02-RECORD-B.
05 IN-ALL-B PIC X(30).
05 IN-END PIC X(270).
01 IN-02-RECORD-C.
05 IN-ALL-C PIC X(30).
05 IN-END-C PIC X(70).
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.
I will read a sequential file which include some string such as "79.85", "1000", "212.34".
I want to convert the alphanumeric into number in this format 00000.00 ?
I will need to add up these numbers and move it to a field in the format 0000000.00 .
I tried:
01 WS_AMOUNT_TXT PIC X(8).
01 WS_AMOUNT PIC 9(5).9(2).
MOVE WS_AMOUNT_TXT(1:8) TO WS_AMOUNT(1:8).
What I got is unexpected, the string is just as same. It is left align and no leading zero display.
How can I made it right align and have leading zero?
EDIT: I tried the suggestion by NealB, and it sadly failed:
01 WS_AMOUNT_NUM PIC 9(5)V9(2).
01 WS_AMOUNT_DISPLAY PIC 9(5).9(2).
01 WS_AMOUNT_TXT PIC X(8).
DISPLAY WS_AMOUNT_TXT
COMPUTE WS_AMOUNT_NUM = FUNCTION NUMVAL (WS_AMOUNT_TXT)
MOVE WS_AMOUNT_NUM TO WS_AMOUNT_DISPLAY
79.85 << this is what was displayed when I called DISPLAY WS_AMOUNT_TXT
AND THEN IT CRASHED.
%COB-F-NUMVALARGINV, NUMVAL or NUMVAL-C argument invalid
%TRACE-F-TRACEBACK, symbolic stack dump follows
image module routine line rel PC abs PC
DEC$COBRTL 0 000000000001F2B8 000000007C2F72B8
DEC$COBRTL 0 0000000000014764 000000007C2EC764
DEC$COBRTL 0 0000000000014C44 000000007C2ECC44
DAILY_SPLIT_REFUND_ADJ DAILY_SPLIT_REFUND_ADJ DAILY_SPLIT_REFUND_ADJ
121 00000000000003C4 00000000000303C4
DAILY_SPLIT_REFUND_ADJ 0 00000000000313A0 00000000000313A0
0 FFFFFFFF80271EF4 FFFFFFFF80271EF4
Try using the intrinsic function NUMVAL
to do the conversion. Something like...
01 WS-AMOUNT-TEXT PIC X(8).
01 WS-AMOUNT-NUM PIC 9(5)V9(2).
01 WS-AMOUNT-DISPLAY PIC 9(5).9(2).
COMPUTE WS-AMOUNT-NUM = FUNCTION NUMVAL (WS-AMOUNT-TEXT)
MOVE WS-AMOUNT-NUM TO WS-AMOUNT-DISPLAY
NUMVAL converts the text representation of a number into a numeric type. Use the numeric data type: PIC 9(5)V9(2) in your calculations. Then use MOVE
to convert the numeric result into a displayable amount with explicit decimal point.
Note: If you have a lot of calculations to perform, it might be best to use a PACKED-DECIMAL data type to improve efficiency.
Use redefine.
01 WS-AMOUNT-TXT-GRP.
03 WS-AMOUNT PIC X(4).
01 WS-AMOUNT-NUM REDEFINES WS-AMOUNT-TXT-GRP PIC 9(4).
After re=licating value in WS-AMOUNT-TXT-GRP or in WS-AMOUNT, automatically value will replicate in WS-AMOUNT-NUM
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.