0

Is that possible to generate a COMP-3 Field in Java?

I'm doing a process that a I need to send a txt file to Mainframe and for bether processing it used a COMP-3 field, to avoid a conversion process in COBOL, I'm trying to generate this format in JAVA, is that possible?

Its for a batch process, that a I need to sendo to Mainframe many registries with value, and the value I want to generate in COMP-3 in JAVA.

Is that possible to do the reverse to? Unpack a COMP-3 field in java, to BigD or String.

How can I do this?

Thanks!

1 Answers1

2

A COBOL COMP-3 is a packed decimal field. The last byte is the sign field. The value x'C' represents a positive value, the value x'D' represents a negative value, and the value x'F' represents an unsigned value.

A COMP-3 field has a specific number of digits and an implied decimal point. You need to know the number of digits and where the implied decimal point is, so your numbers will be interpreted correctly in the COBOL program.

A COMP-3 field always has an even number of bytes. Most COBOL developers recognize this and create COMP-3 PICTURE clauses with an odd number of digits.

Let's take an example of 200 and a COBOL PICTURE of +999 COMP-3 and convert it to a packed decimal.

200 -> x'200C'

Let's take an example of -125.125 and a COBOL PICTURE of -999V999 COMP-3 and convert it to a packed decimal.

-125.125 -> x'0125125D'

Basically, you take each digit of the int or double, move the digit into a byte, and shift the byte into the proper position.

I went ahead and wrote two conversion methods since it's obvious that these conversions are so complex that only an experienced COBOL/Java developer can possibly code them.

Here is one of my test results.

5000.25
0 0 0 0 0 0 0 0 0 5 0 0 0 2 5 c 
5000.25

-12000.4
0 0 0 0 0 0 0 0 1 2 0 0 0 4 0 d 
-12000.4

The first value is the input double.

The second value is the packed decimal String, displayed as a series of hexadecimal values.

The third value is the result of converting the packed decimal String back to a double.

Here's the complete runnable code.

public class COMP3Conversions {

    public static void main(String[] args) {
        COMP3Conversions cc = new COMP3Conversions();
        convert(cc, 5000.25);
        convert(cc, -12000.40);
    }

    private static void convert(COMP3Conversions cc, double value) {
        System.out.println(value);
        // Since the COBOL PICTURE is S9(13)V9(2), the output will be 16 bytes.
        String s = cc.toComp3(value, 13, 2);
        char[] result = s.toCharArray();
        for (char c : result) {
            System.out.print(Integer.toHexString((int) c) + " ");
        }
        System.out.println();
        double answer = cc.toDouble(s, 13, 2);
        System.out.println(answer);
        System.out.println();
    }
    
    /**
     * This method converts a double to a COBOL COMP-3 packed decimal. We use a
     * <code>String</code> to hold the packed decimal as the maximum COBOL
     * packed decimal value exceeds the size of a <code>long</code>.
     * 
     * @param value            - Ihe value to be converted
     * @param digits           - The number of digits to the left of the implied
     *                         decimal point.
     * @param fractionalDigits - The number of digits to the right of the
     *                         implied decimal point.
     * @return A string with the packed decimal
     */
    public String toComp3(double value, int digits, int fractionalDigits) {
        int totalDigits = digits + fractionalDigits + 2;
        String formatString = "%+0" + totalDigits + "." + fractionalDigits + "f";
        String valueString = String.format(formatString, value);
        valueString = valueString.replace(".", "");
        
        StringBuilder builder = new StringBuilder();
        char[] digitChars = valueString.toCharArray();
        for (int index = 1; index < digitChars.length; index++) {
            char c = digitChars[index];
            int digit = Integer.valueOf(Character.toString(c));
            char d = (char) digit;
            builder.append(d);
        }
        
        if (digitChars[0] == '+') {
            builder.append((char) 0xC);
        } else if (digitChars[0] == '-') {
            builder.append((char) 0xD);
        } else {
            builder.append((char) 0xF);
        }
        
        return builder.toString();
    }
    
    /**
     * This method converts a COBOL COMP-3 field represented as a String to a
     * <code>double</code> value.
     * 
     * @param value            - COBOL COMP-3 String
     * @param digits           - The number of digits to the left of the implied
     *                         decimal point.
     * @param fractionalDigits - The number of digits to the right of the
     *                         implied decimal point.
     * @return Java <code>double</code> value
     */
    public double toDouble(String value, int digits, int fractionalDigits) {
        char[] digitChars = value.toCharArray();
        int sign = (int) digitChars[digitChars.length - 1];
        
        int digitIndex = 0;
        StringBuilder builder = new StringBuilder();
        for (int index = digitChars.length - 2; index >= 0; index--) {
            if (digitIndex == fractionalDigits) {
                builder.append('.');
            }
            String s = Integer.toString((int) digitChars[index]);
            builder.append(s);
            digitIndex++;
        }
        
        double result = Double.valueOf(builder.reverse().toString());
        if (sign == 0xD) {
            result *= -1;
        }
        
        return result;
    }

}
Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111
  • thanks a lot, for this I working with a COBOL PICTURE of PIC S9(13)V9(2) COMP-3. Can you please share any example or material of how can I do this in code? – Italo Vieira Dec 12 '22 at 21:06
  • @Italo Vieira: See my updated answer. – Gilbert Le Blanc Dec 12 '22 at 22:55
  • 1
    @ItaloVieira Please note that you would have to generate all alphanumeric fields with EBCDIC-encoding and transfer the file to the mainframe in binary - otherwise the automatic conversion will destroy the COMP-3 fields. – piet.t Dec 13 '22 at 07:26
  • @GilbertLeBlanc I might be mistaking, but this demonstrates a different format than the 'packed decimal', as there is one detail which I assume to be different. Since 1 byte has the value-range of 0-255, in packed decimal the value-range of one digit should only be 0-15. Thus I think for every digit, one is skipped. In other words, w/ the code a decimal of 7 would be reflected by picture of two bytes `X'070F'` instead of one byte `X'7F'`as expected to be binary equivalent? – MJG Dec 23 '22 at 15:22
  • @MJG: That's why I move the packed decimal to a display field, and back to a packed decimal. In a display number, each digit is one character. – Gilbert Le Blanc Dec 23 '22 at 23:00