javacobolpacked-decimal

COBOL Comp-3 In Java


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!


Solution

  • 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;
        }
    
    }