Wednesday, June 20, 2018

Different types of numbers in RPG

using different types of number fields in rpg

For those of you like me who have been programming in IBM i and its predecessors for some time this might seem a simple subject matter. But I have received quite a few questions regarding numbers in the past few weeks. So I thought I would write something answering those questions and a bit more about the different types of numbers you will find in IBM i.

The most basic types of numbers everyone who has programmed on an IBM i has used are packed and zoned numbers. Zoned numbers are not anything new they are just the name that is used for what we use to call signed numbers. What is the difference? Why would I want to use packed rather than zoned? It really comes down to space. Back when computer first started using disk storage it was very expensive, therefore, everyone tried to reduce the amount of disk space they use. Someone came up with the idea of "packing" numbers to reduce the amount of space they use. "Packing" is a method I can store two numbers in each byte. Rather than spend paragraphs explaining what this is I think an example, like a picture, paints a thousand words.

Below is a very simple program that moves numbers into packed and zoned numbers.

01  **free
02  dcl-s Packed6 packed(6) ;
03  dcl-s Packed6a like(Packed6) ;
04  dcl-s Zoned6 zoned(6) ;
05  dcl-s Zoned6a like(Zoned6) ;

06  Packed6 = 123456 ;
07  Zoned6 = 123456 ;

08  Packed6a = -123 ;
09  Zoned6a = -123 ;

10  dump(a) ;
11  *inlr = *on ;

Line 1: I only program in totally free RPG.

Lines 2 – 5: These are the variables I will be using. The first two are defined as packed variables, lines 2 and 3. The other two, lines 4 and 5, are defined as zoned, signed.

Lines 6 – 7: I move a positive number to these variables.

lines 8 and 9: I move negative numbers to these variables.

Line 10: I want to produce a program dump so that we can see what these variables hold.

This is copied from the program dump listing for the program's variables.

PACKED6   PACKED(6,0)  123456.   '0123456F'X
PACKED6A  PACKED(6,0)  -000123.  '0000123D'X
ZONED6    ZONED(6,0)   123456.   'F1F2F3F4F5F6'X
ZONED6A   ZONED(6,0)   -000123.  'F0F0F0F1F2D3'X

The part I am really interested is the hexadecimal value at the end of each line. This does show the value in each byte of the variable, but, in my opinion it is easier to understand if I format it like this:

Variable type Positive value Negative value
Packed 0246

The first thing to notice is that the zoned variable is six bytes long, while the packed is four.

Why isn't the packed variable three long if you can store two number is each byte? You can store two numbers in each byte except for the last byte in a packed variable. The last half of the last byte contains a character that denotes the sign of the value. "F" indicates a positive value, "D" a negative. This makes all packed variables an odd value long. I can define the variable to be an even number, as I did in this example, but half a byte goes to waste. A wise old programmer told me when I first started learning RPG that all packed variables should always be odd numbers so that you do not waste that first half of a byte.

The sign code in a zoned variable is the first half of the last byte, as shown in the table above.

Over the years various other types of numbers have been introduced to the RPG language. As of IBM i 7.3 the current number types are:

Number type Keyword Maximum size
Packed packed Up to 63
Zoned zoned Up to 63
Integer int 3, 5, 10, 20 1
Unsigned integer uns 3, 5, 10, 20 1
Binary bindec Up to 9
Float Float 4 or 8

1 Integer and unsigned cannot have decimal places.

To me the next question would be: what is the largest value these types of numbers can hold? The program below has variables defined of the various types and lengths. It move RPG hexadecimal value *HIVAL to each variable, which will be translated to variables maximum value.

01  **free
02  dcl-s Packed packed(63:0) ;
03  dcl-s Zoned zoned(63) ;
04  dcl-s Int3 int(3) ;
05  dcl-s Int5 int(5) ;
06  dcl-s Int10 int(10) ;
07  dcl-s Int20 int(20) ;
08  dcl-s Uns3 uns(3) ;
09  dcl-s Uns5 uns(5) ;
10  dcl-s Uns10 uns(10) ;
11  dcl-s Uns20 uns(20) ;
12  dcl-s Binary bindec(9) ;
13  dcl-s Float4 float(4) ;
14  dcl-s Float8 float(8) ;

15  Packed = *hival ;
16  Zoned = *hival ;
17  Int3 = *hival ;
18  Int5 = *hival ;
19  Int10 = *hival ;
20  Int20 = *hival ;
21  Uns3 = *hival ;
22  Uns5 = *hival ;
23  Uns10 = *hival ;
24  Uns20 = *hival ;
25  Binary = *hival ;
26  Float4 = *hival ;
27  Float8 = *hival ;

28  dump(a) ;
29  *inlr = *on ;

I have sorted the results from the dump into the order I defined the variables in the program:

ZONED    ZONED(63,0)
INT3     INT(3)         127
INT5     INT(5)         32767
INT10    INT(10)        2147483647
INT20    INT(20)        9223372036854775807
UNS3     INT(3)         255
UNS5     INT(5)         65535
UNS10    INT(10)        4294967295
UNS20    INT(20)        18446744073709551615
BINARY   BIN(9,0)       999999999
FLOAT4   FLT(4)         3.402823466385E+038
FLOAT8   FLT(8)         1.797693134862E+308

I did not bother to put in the maximum value for the packed and zoned field as it is 63 9s, or 9 x 1063, which is too big to fit reasonably on this page.

Today's piece of useless information:  1063 is called 1 vigintillion.

If I replace the *HIVAL with *LOVAL I get the opposite, the lowest possible number in each variable.

ZONED    ZONED(63,0)
INT3     INT(3)         -128
INT5     INT(5)         -32768
INT10    INT(10)        -2147483648
INT20    INT(20)        -9223372036854775808
UNS3     INT(3)         0
UNS5     INT(5)         0
UNS10    INT(10)        0
UNS20    INT(20)        0
BINARY   BIN(9,0)       -999999999
FLOAT4   FLT(4)         -3.402823466385E+038
FLOAT8   FLT(8)         -1.797693134862E+308

I have omitted the value for the packed and zoned as it is just the negative value of the positive value, and too many nines for this page.

The thing that caught my eye is that the negative value for the integer is one "smaller" than the positive value. For example, Int3 maximum value is 127, minimum value is -128!

What about moving the values between the types of numbers. It is really very simple, as they are all numbers we can move from one number type to another just with a standard expression:

01  **free
02  dcl-s Packed packed(7:2) ;
03  dcl-s Zoned zoned(5) ;
04  dcl-s Int5 int(5) ;
05  dcl-s Uns5 int(5) ;
06  dcl-s Binary bindec(9:2) ;
07  dcl-s Float4 float(4) ;

08  Packed = 123.45 ;
09  Float4 = Packed ;
10  Int5 = Float4 ;
11  Zoned = Int5 ;
12  Uns5 = Zoned ;
13  Binary = Uns5 ;

14  dump(a) ;
15  *inlr = *on ;

When I sort the variables in order from the dump listing this is what I find:

PACKED    00123.45
FLOAT4    1.234499969482E+002
INT5      123
ZONED     00123.
UNS5      123
BINARY    0000123.00

I find it interesting that the float equivalent of 123.45 is not 1.2345E+002.

When the float is moved into the integer variable the decimal places are lost, therefore, they cannot be copied into the zoned or binary variables.

RPG does give a number of built in functions designed for being used when moving values into numeric variables:

As the result of all of these built in functions is a number it should come as no surprise that I can use any of them to move a value into any of the number types.

Packed = %int('123.45') ;
Int5 = %dec('123.45':5:2) ;
Zoned = %uns('123.45') ;
Uns5 = %float(Packed) ;

The results are what I would have expected.

PACKED   0000123.00
INT5     123
ZONED    00123.
UNS5     123


OK, I have gone on long enough about numbers in RPG programs. I also want to talk about numbers in DDS file and Db2 for i DDL tables, and that will be the subject of the next post.


This article was written for IBM i 7.3, and should work for earlier releases too.


  1. It looks like in your example with unsigned variables you defined them with INT instead of UNS. This is also reflected in the maximum values shown in the dump.

    1. Thank you for letting me know.
      Correction has been made.

  2. Hello Simon.
    Thanks again for the post.
    One remark: you declared the Unsigned variables as int type. That is probably why you get the same values (and even negative ones) as the Int variables.

    Continue with your good work.

    1. Good catch, that explains why I get negative numbers. How can I have missed that!

      The code has been corrected, and the result is the *LOVAL for an unsigned integer is zero.

      Thank you for letting me know my mistake.

  3. This reminds me of working on Decision Data minis back in 1979, when our online system was written in Fortran V and the back end was written in Cobol.
    Fortran V could store a larger value in a Floating Decimal variable than you could store in a variable of the same length in Cobol, so every numeric field that was read from a file in Cobol had to be moved to a character field, have an extra character added to the left, and then redefined as a numeric field (or something like that).
    Painful to program, and of course the reverse was true when you had to write numeric values back to a file.
    Those were the days...

  4. Very comprehensive, great goto reference for the future. Thanks.
    Hey, in your spare time, how about a tribute to the Classic RPG Edit Code, which most of the rest of the world has never enjoyed the luxury of..?

  5. Really helpful. Thanks. You said that you will talk about numbers in DDS file in another post. Is that available?? I tried to search for it but can't find.

    1. I do not have anything like that for DDS files.

      My thoughts are as DDS are no longer going to be enhanced, and the move (and guidance from IBM) is to move to DDL I concentrate my efforts there.

  6. Why do I sometimes see 'count int(10:0);' ? Why do some people add a decimal size to an integer definition?

    1. Because the number of decimal places is optional for INT and UNS definitions.


To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.