HLASM Packed Decimal Arithmetic: AP, SP, MP, DP, CP, PACK, UNPK

TT

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:

hlasm
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 bytes

PACK 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.

hlasm
* 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 arithmetic

UNPK — Convert Packed to Zoned

UNPK zoned(len),packed(len) reverses the process:

hlasm
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:

hlasm
TOTAL    DC    PL5'0'           5-byte packed total, initially zero
AMOUNT   DC    P'12345'
         AP    TOTAL,AMOUNT     TOTAL = TOTAL + AMOUNT

Both 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).

hlasm
* 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 total

SP — Subtract Packed

hlasm
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):

hlasm
* 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 * PRICE

Rule: 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:

hlasm
* 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):

hlasm
         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:

hlasm
SMALLPCK DC    P'99'
BIGFIELD DS    PL8
         ZAP   BIGFIELD,SMALLPCK   Copy with zero-extension to larger field

Practical: Invoice Calculation

hlasm
* 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 = 59500

Frequently 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.