HLASM Packed Decimal Arithmetic: AP, SP, MP, DP, CP, PACK, UNPK
HLASM Packed Decimal Arithmetic: AP, SP, MP, DP, CP, PACK, UNPK
Packed decimal is the format of choice for financial arithmetic on z/OS. It provides exact decimal representation without the rounding errors of binary floating-point. This module covers the full set of packed decimal instructions.
PACK — Convert Zoned to Packed
PACK packed(len),zoned(len) converts a zoned decimal field to packed format:
ZONED DC Z'12345' 5-byte zoned: F1 F2 F3 F4 C5
PACKED DS PL3 3-byte packed result
PACK PACKED,ZONED Result: X'012345C' in 3 bytes? No:
* Actually: X'12345C' in 3 bytesPACK strips the zone nibbles and compresses two digits per byte. The sign nibble from the last zoned byte becomes the sign in the packed result.
* Convert input data (COBOL DISPLAY field) to packed for arithmetic
INPUTAMT DS ZL7 7-digit COBOL PIC 9(7) DISPLAY
WRKAMT DS PL4 4-byte packed work field
PACK WRKAMT,INPUTAMT Convert for arithmeticUNPK — Convert Packed to Zoned
UNPK zoned(len),packed(len) reverses the process:
RESULT DS PL4 Packed result from calculation
OUTPUT DS ZL7 7-byte output for display
UNPK OUTPUT,RESULT Convert packed to zoned
OI OUTPUT+6,X'F0' Fix sign: set zone of last byte to F
* (UNPK puts the sign nibble as the zone)The OI instruction is needed because UNPK preserves the sign nibble (C, D, or F) as the zone of the last byte. To get a printable unsigned digit, force it to X'F'.
AP — Add Packed
AP destination(dlen),source(slen) adds two packed decimal fields:
TOTAL DC PL5'0' 5-byte packed total, initially zero
AMOUNT DC P'12345'
AP TOTAL,AMOUNT TOTAL = TOTAL + AMOUNTBoth fields must be valid packed decimal. The result goes in the destination. If the result overflows the destination length, a decimal overflow program check occurs (0CA abend).
* Accumulate a running total
RUNTOTAL DS PL7'0' 7-byte running total
ITEM DS PL4 Current item amount
AP RUNTOTAL,ITEM Add item to running totalSP — Subtract Packed
BALANCE DC P'10000'
PAYMENT DC P'3500'
SP BALANCE,PAYMENT BALANCE = BALANCE - PAYMENT (= 6500)MP — Multiply Packed
MP destination(dlen),source(slen) multiplies packed fields. The destination must be large enough for the result (sum of digits):
* Multiply quantity by unit price
QTY DC P'150' 3 digits
PRICE DC P'2500' 4 digits (cents)
TOTAL DS PL8 Must hold up to 7 digits (3+4) result
ZAP TOTAL,QTY Zero and add: copy QTY to TOTAL field
MP TOTAL,PRICE TOTAL = TOTAL * PRICERule: if destination has m digits and source has n digits, the result field must have at least m+n-1 digits (bytes). The source field must be shorter than the destination.
DP — Divide Packed
DP destination(dlen),source(slen) divides. The result is stored as quotient followed by remainder in the destination field:
* Divide total cents by 100 to get dollars and cents
CENTS DC P'123456' $1234.56 in cents
DIVISOR DC P'100'
WORK DS PL8 Work area for quotient + remainder
ZAP WORK,CENTS Copy dividend to work area
DP WORK,DIVISOR WORK = quotient (front) + remainder (back)
* quotient is in WORK(dlen-slen) = WORK(6) — first 6 bytes
* remainder is in WORK+6(slen) = WORK+6(2) — last 2 bytes (length of DIVISOR)CP — Compare Packed
CP field1(len),field2(len) compares two packed fields algebraically (signed):
CP BALANCE,MINIMUM Compare balance to minimum
BL LOWBAL Branch if balance < minimum
BE EXACT Branch if balance == minimum
BH ADEQUATE Branch if balance > minimum
BALANCE DC P'5000'
MINIMUM DC P'1000'ZAP — Zero and Add Packed
ZAP destination(dlen),source(slen) zeroes the destination then adds the source. Used to copy a packed field to a larger destination:
SMALLPCK DC P'99'
BIGFIELD DS PL8
ZAP BIGFIELD,SMALLPCK Copy with zero-extension to larger fieldPractical: Invoice Calculation
* Calculate: TOTAL = (QUANTITY * UNIT_PRICE) - DISCOUNT
QTY DC P'50' 50 items
PRICE DC P'1200' $12.00 each (in cents)
DISCOUNT DC P'500' $5.00 discount (in cents)
SUBTOTAL DS PL8 Work area
TOTAL DS PL8
*
ZAP SUBTOTAL,QTY Copy quantity to large work area
MP SUBTOTAL,PRICE SUBTOTAL = 50 * 1200 = 60000
ZAP TOTAL,SUBTOTAL Copy to result
SP TOTAL,DISCOUNT TOTAL = 60000 - 500 = 59500Frequently Asked Questions
Q: Why use packed decimal instead of binary (fullword) for financial arithmetic? Binary integers represent values exactly only for integers. When converting between binary and decimal, rounding errors can occur for values that are exact in decimal but repeating in binary. Financial applications require exact decimal arithmetic — $12.34 must be stored and computed as exactly 1234 cents. Packed decimal arithmetic on z/OS is exact to the number of digits defined, with no binary rounding. The packed decimal instructions are also hardware-accelerated on IBM Z.
Q: How do I handle packed decimal overflow? By default, decimal overflow causes a 0CA program interrupt (abend). To prevent this, ensure your result fields are large enough before performing arithmetic. A good practice: for addition, the result field should have one more digit than the larger operand. For multiplication, the result field should have digits equal to the sum of both operand digit counts. You can also use the SPM instruction to mask the decimal overflow interrupt, but that can hide real errors.
Q: What is the difference between ZAP and PACK?
Both can move a value into a packed field. PACK packed,zoned converts from zoned decimal (COBOL DISPLAY) to packed. ZAP dest,source copies one packed field to another (possibly larger) destination, zero-extending the high-order digits. Use PACK when your source is a zoned/display field. Use ZAP when your source is already packed and you need to copy it to a wider field for arithmetic.
Part of HLASM Mastery Course — Module 12 of 22.
