Read / Write COBOL signed value Tag(s): Number
Output a signed value
There is a common numeric data type used in COBOL called "Signed" (also called "IBM Signed", or "Zoned"). COBOL represents this type of field by an "S" in the picture clause of a display format field, e.g. PIC S9(6).
A Signed field is composed of regular numeric characters, one character per byte, for all digits except the one that holds the sign. The digit that holds the sign combines, or "over punches" the sign of the number onto that digit. This saves one byte that the sign would otherwise occupy. The value of that digit is stored as a binary value, and is OR'd with the sign code, which is D0 hex for negative numbers, C0 hex for positive values.
If you have the field
05 ACCOUNT-BALANCE PIC S9(6)V99.and view a value of 1.23 will read "0000012C". ref : http://www.3480-3590-data-conversion.com/article-reading-cobol-layouts-4.html
import java.math.BigDecimal; public class CobolUtils { /** * Pad with zero a given number * @param num string representation of a number * @param w the total number of digit * @param d number of digit after the decimal point * @param signed is it a signed number * @return the zero-padded number as a string, COBOL number (signed or not) */ public static String padNum(String num, int w, int d, boolean signed) { if (num == null) { return padNum(0, w, d, signed); } else { return padNum(Double.parseDouble(num), w, d, signed); } } /** * Pad with zero a given number * @param num the number * @param w the total number of digit * @param d number of digit after the decimal point * @param signed is it a signed number * @return the zero-padded number s string, COBOL number (signed or not) */ public static String padNum(double num, int w, int d, boolean signed) { // decimal point String format1 = "%." + d + "f"; String temp = String.format(format1, num); temp = temp.replace(",", "").replace(".", ""); // remove decimal point and separator int i = Integer.parseInt(temp); if (i <= 0) { w++; // } // leading zeroes String format2 = String.format("%%0%dd", w + d); return (!signed) ? String.format(format2, i) : toCobolSignedString(String.format(format2, i)); } /** * Build a string representation using the COBOL PIC S9()... style representation * Ascii based, not EBCDIC * @param snumber * @return string */ public static String toCobolSignedString(String snumber) { int i = Integer.parseInt(snumber); char lastChar = snumber.charAt(snumber.length() - 1 ); char value = (i >= 0) ? CobolUtils.getChar(true, lastChar) : CobolUtils.getChar(false, lastChar); StringBuffer sb = new StringBuffer(snumber); sb.setCharAt(sb.length() - 1, value); return (i >= 0) ? sb.toString() : sb.toString().substring(1); // if LT 0, remove the minus } /** * Returns a double from Cobol signed representation PIC S9()V9... * Ascii based, not EBCDIC * @param snumber * @param digit number of digit after the decimal point * @return double */ public static double fromCobolSignedString(String snumber, int digit) { String value; char lastdigit = snumber.charAt(snumber.length() - 1); if (CobolUtils.isPositiveOverpunch(lastdigit)) { char reallastdigit = CobolUtils.getNumber(lastdigit); value = snumber.substring(0, snumber.length() - 1) + String.valueOf(reallastdigit); } else { char reallastdigit = CobolUtils.getNumber(lastdigit); value = "-" + snumber.substring(0, snumber.length() - 1) + String.valueOf(reallastdigit); } if (digit > 0) { value = value.substring(0, value.length() - digit + 1) + "." + value.substring(value.length() - digit); } BigDecimal bd = new BigDecimal(Double.parseDouble(value)); bd = bd.setScale(digit,BigDecimal.ROUND_HALF_UP); return bd.doubleValue(); } public static void main(String[] args) { // num to cobol System.out.println(CobolUtils.padNum("-60398.74", 9, 2, true)); System.out.println(CobolUtils.padNum(-60398.74, 9, 2, true)); System.out.println(CobolUtils.padNum("60398.74", 9, 2, true)); System.out.println(CobolUtils.padNum("42000", 5, 0, true)); System.out.println(CobolUtils.padNum("-42000", 5, 0, true)); System.out.println(CobolUtils.padNum("1.23", 6, 2, true)); System.out.println(CobolUtils.padNum(1.23d, 6, 2, true)); // "-60398.74" --> "0000603987M" // -60398.74 --> "0000603987M" // "60398.74" --> "0000603987D" // "42000" --> "4200{" // "-42000" --> "4200}" // "1.23" --> "0000012C" // 1.23 --> "0000012C" System.out.println(CobolUtils.toCobolSignedString("123")); // "123" --> 12C // cobol to num System.out.println(CobolUtils.fromCobolSignedString("4200{", 0)); System.out.println(CobolUtils.fromCobolSignedString("4200}", 0)); System.out.println(CobolUtils.fromCobolSignedString("603987D", 2)); System.out.println(CobolUtils.fromCobolSignedString("603987M", 2)); // "4200{" --> 42000 // "4200}" --> -42000 // "603987D" --> 60398.74 // "603987M" --> -60398.74 } // ref : https://github.com/ethiclab/cb2java/blob/master/src/net/sf/cb2java/types/Decimal.java private static char getNumber(char overpunch) { switch(overpunch) { case 'R': case 'I': return '9'; case 'Q': case 'H': return '8'; case 'P': case 'G': return '7'; case 'O': case 'F': return '6'; case 'N': case 'E': return '5'; case 'M': case 'D': return '4'; case 'L': case 'C': return '3'; case 'K': case 'B': return '2'; case 'J': case 'A': return '1'; case '}': case '{': return '0'; } throw new IllegalArgumentException("invalid char for a signed number: " + overpunch); } private static char getChar(boolean positive, char overpunch) { if (positive) { switch(overpunch) { case '0': return '{'; case '1': return 'A'; case '2': return 'B'; case '3': return 'C'; case '4': return 'D'; case '5': return 'E'; case '6': return 'F'; case '7': return 'G'; case '8': return 'H'; case '9': return 'I'; } } else { switch(overpunch) { case '9': return 'R'; case '8': return 'Q'; case '7': return 'P'; case '6': return 'O'; case '5': return 'N'; case '4': return 'M'; case '3': return 'L'; case '2': return 'K'; case '1': return 'J'; case '0': return '}'; } } throw new IllegalArgumentException("invalid number: " + overpunch); } private static boolean isPositiveOverpunch(char overpunch) { switch(overpunch) { case '{': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': return true; case '}': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': return false; } throw new IllegalArgumentException("invalid char for a signed number: " + overpunch); } }
mail_outline
Send comment, question or suggestion to howto@rgagnon.com
Send comment, question or suggestion to howto@rgagnon.com