In DFSORT INCLUDE and OMIT, when the field you are testing is numeric—amounts, counts, IDs, or any value stored in a numeric format—you must specify the correct format in the condition so that DFSORT interprets the bytes as a number and compares by numeric value. The most common numeric formats in conditions are PD (packed decimal), ZD (zoned decimal), and BI (binary). Each has a different storage layout: packed decimal packs two digits per byte with a sign in the rightmost half-byte; zoned decimal uses one byte per digit with the sign in the last byte; binary is fixed-length two's complement. If you use CH (character) on a numeric field, the comparison is byte-by-byte by collating sequence, not by numeric value, and you can get wrong or unexpected results. This page explains how to use PD, ZD, and BI in COND=, how to choose the length, and how to write the comparison value (including negatives) so your numeric filters work correctly.
The same numeric value can be stored in different ways on the mainframe. For example, the number 12345 might be stored as packed decimal (bytes like x'12345C'), zoned decimal (one byte per digit with a zone and digit in each byte), or as a binary fullword. When you write COND=(start, length, format, operator, value), the format tells DFSORT how to decode those bytes into a number. If you use CH, DFSORT does not decode—it compares raw bytes. So the "number" 12345 as CH might compare incorrectly to 12345 as a constant because the byte representation is different. Always use the format that matches the way the field is actually stored in the record.
| Format | Name | Typical length (bytes) | Use |
|---|---|---|---|
| PD | Packed decimal | Variable (e.g. 2–8) | Signed decimal integers; 2 digits per byte + sign |
| ZD | Zoned decimal | Variable (1 digit per byte) | Signed decimal; sign in last byte (C/D etc.) |
| BI | Binary | 2 (halfword) or 4 (fullword) | Integer; fullword = 4 bytes |
| FI | Fixed point | Product-dependent | Fixed-point numeric; see manual |
Packed decimal (PD) stores two decimal digits per byte, except the rightmost half-byte which holds the sign (e.g. C for positive, D for negative). So a 4-byte PD field holds 7 digits plus sign (e.g. -9999999 to +9999999). In COND= you specify the byte length: (start, length, PD, operator, value). Example: (20,4,PD,GT,0) tests the 4-byte packed field at positions 20–23 and keeps or omits when its value is greater than zero. The value is a numeric constant (e.g. 0, 100, -50). Use PD when your data is stored in packed form (common for amounts and counts in COBOL COMP-3 or equivalent).
Zoned decimal (ZD) stores one digit per byte. The last byte also carries the sign (e.g. C or F for positive, D for negative in EBCDIC). So a 5-byte ZD field holds 5 digits plus sign. In COND= you use (start, length, ZD, operator, value). Example: (30,5,ZD,LE,99999) keeps records where the 5-byte zoned field at 30–34 is less than or equal to 99999. Zoned decimal is common in display or character-like numeric fields. Use ZD when the data is stored in zoned form (e.g. COBOL DISPLAY numeric).
Binary (BI) is fixed-length two's complement (or unsigned, depending on context). A halfword is 2 bytes; a fullword is 4 bytes. In COND= you specify (start, length, BI, operator, value) where length is 2 or 4 (or 8 for doubleword if supported). Example: (40,4,BI,NE,0) tests the 4-byte binary at 40–43 and is true when the value is not zero. For very large or hex constants, your product may allow a special syntax (e.g. X'...'); see the manual. Use BI when the field is stored as binary (e.g. COBOL COMP or COMP-4).
Start is the starting byte position (1-based) of the field in the record. If you use INREC, positions refer to the record after INREC. Length is the number of bytes. For PD, length is the total byte count (e.g. 4 for a 7-digit + sign packed field). For ZD, length is the number of bytes (usually same as digit count for signed). For BI, length is 2 or 4 (or 8). Getting the length wrong can cause incorrect comparisons or abends: too short and you read part of the field; too long and you include the next field. Check your record layout or copybook.
You can compare numeric fields to negative constants. Example: INCLUDE COND=(20,4,PD,LT,0) keeps records where the packed amount is less than zero. For zoned decimal, the sign is in the last byte; DFSORT interprets it and compares correctly. For binary, negative numbers are in two's complement form. Zero is common: (30,4,PD,NE,0) keeps non-zero values; (30,4,PD,EQ,0) keeps only zero. Make sure the constant fits the range of the field (e.g. a 2-byte binary halfword has a limited range).
Keep records where packed-decimal amount at 25–28 is positive:
1INCLUDE COND=(25,4,PD,GT,0)
Omit records where zoned-decimal code at 10–12 is zero:
1OMIT COND=(10,3,ZD,EQ,0)
Keep records where 4-byte binary at 50–53 is greater than or equal to 1:
1INCLUDE COND=(50,4,BI,GE,1)
Keep records where packed amount at 20–23 is between 100 and 500 (use AND):
1INCLUDE COND=(20,4,PD,GE,100,AND,20,4,PD,LE,500)
Numbers can be written in different "languages" on the computer: packed is like writing two digits in one box, zoned is one digit per box, and binary is a different kind of code. When we tell the sort program "is this number bigger than 100?" we have to say which language the number is in (PD, ZD, or BI). If we say the wrong one, the computer reads the wrong thing—like reading a word in the wrong language—and the answer is wrong. So we always pick the format that matches how the number is actually stored.
1. Why must you specify the correct format (PD, ZD, BI) for a numeric field in COND=?
2. What is the length of a 4-byte fullword binary field in COND=?
3. For packed decimal (PD), how is the length specified?
4. Can you compare a zoned decimal (ZD) field to a negative value?
5. What happens if the numeric constant is too large for the field?