Be careful when using SETLL *LOVAL on keyed access paths containing numeric key fields.
Almost every RPG programmer has become accustomed to setting the file pointer to the starting position of a logical file by specifying the SETLL (Set Lower Limit) RPG operation code with figurative constant *LOVAL as the search argument operand, or similarly, setting the file pointer to the end of a logical file by specifying SETGT (Set Greater Than) with *HIVAL. The SETLL *LOVAL technique worked perfectly for decades, doing exactly what we RPG programmers expected. But is it always to be trusted everywhere?
The ILE RPG Language Reference manual mentions a few special conditions in which the SETLL *LOVAL will not work as it's supposed to. In this article, I will introduce you to things to consider before using SETLL *LOVAL.
- It's a programming error to position to the beginning of a keyed physical or logical file containing numeric key fields with the UNSIGNED or ABSVAL keyword specified by using SETLL *LOVAL.
- For a keyed PF or LF that has a floating-point key field, a SETLL *LOVAL operation cannot locate a record the value of whose float key field is -INF (negative infinity) or +INF (positive infinity).
What's the Actual Value of *LOVAL?
*LOVAL is an RPG figurative constant that can be specified as the value of the definition specification keyword INZ or the search-argument operand of operation code SETLL or SETGT. The actual value of *LOVAL is determined by the RPG compiler at compile time according to the actual data type of the data object being represented by *LOVAL. Using the following simple experiment, you can discover the actual values of *LOVAL determined for different numeric data types by the RPG compiler.
1. Set up a LF with three numeric key fields of type PKD(5,0), ZND(5,0), and 9-digit binary (4-byte binary), respectively.
2. Write a simple OPM RPG program that issues SETLL *LOVAL on the LF.
3. Compile the OPM RPG program with parameter GENOPT(*LIST).
Now, dig into the Generated Output section of the compiler listing, where you can find the MI instructions to initialize the actual key value represented by *LOVAL, like the following:
CPYBRAP .KEY(0001:0003), X'9D',X'99' /* SET KEY FIELD */ /*ZFIGCPY*/ ; CPYBRAP .KEY(0004:0005), X'D9',X'F9' /* SET KEY FIELD */ /*ZFIGCPY*/ ; CPYBLA .KEY(0009:4), X'C4653601' /* BIN(4) KFLD */ /*ZB4FIG*/ ; |
The above MI instructions copy the actual values determined by the RPG compiler (for the PKD(5,0), ZND(5,0), and BIN(4) key fields) into character variable .KEY, which will be then used as the actual search argument of SETLL. The result value of .KEY is x'99999DF9F9F9F9D9C4653601'. So now you know the actual values that *LOVAL is translated into for different types of numeric data types:
- x'9999...9D' (-99...9) for packed decimal
- x'F9F9...D9' (-99...9) for zoned decimal
- x'FFF7' (-9) for 1-digit binary, x'FF9D' (-99) for 2-digit binary, and so on. For example, x'D8F1' (-9,999) for 4-digit binary (2-byte binary) and x'C4653601' (-999,999,999) for 9-digit binary (4-byte binary).
Additionally, according to the ILE RPG Reference, *LOVAL is:
- x'FF7FFFFF' (-3.4028234E+038) for 4-byte floating-point
- x'FFEFFFFFFFFFFFFF' (-1.797693134862315E+308) for 8-byte floating-point
Similarly, you can find out the actual value of *HIVAL determined by the RPG compiler for different types of numeric data types.
Actual Value of *HIVAL |
||
Data Type |
Value |
Hexadecimal Value |
Packed decimal |
+99...9 |
x'9999...9F' |
Zoned decimal |
+99...9 |
x'F9F9...F9' |
N-digit binary |
+99...9 (repeated for N times) |
1-digit binary: x'0009' 2-digit binary: x'0063' ... |
4-byte floating point |
+3.4028235E+38 |
x'7F7FFFFF' |
8-byte floating point |
+1.797693134862315E+308 |
x'7FEFFFFFFFFFFFFF' |
SETLL *LOVAL and Numeric Key Fields with the UNSIGNED Keyword Specified
Key fields defined as character fields (character, date, time, timestamp, and hexadecimal fields) are arranged based on the sequence defined for EBCDIC characters. Key fields defined as numeric fields are arranged based on their algebraic values, unless the UNSIGNED (unsigned value) or ABSVAL (absolute value) DDS keywords are specified for the field.
UNSIGNED is a key field-level keyword to specify that numeric fields are sequenced as a string of unsigned binary data. Note that the UNSIGNED keyword will be the default in the following situations:
- When you specify ALTSEQ at the file level for a zoned key field
- When you specify ZONE or DIGIT for a zoned key field
- For all character and hexadecimal fields
Assume that you have a physical file LOVAL01 whose DDS source is the following:
R REC A 8A COLHDG('CHAR_8') PKD 5P 0 COLHDG('PKD_5_0') ZND 5S 0 COLHDG('ZND_5_0') BIN4 9B 0 COLHDG('BIN_4') FLT4 9F 0 COLHDG('FLT_4') |
There are three records in PF LOVAL01, like the following:
LOVAL01 |
|||||
RRN |
CHAR_8 |
PKD_5_0 |
ZND_5_0 |
BIN_4 |
FLT_4 |
000001 |
REC-1 |
5- |
5- |
5- |
-500000000.E-008 |
000002 |
REC-2 |
0 |
0 |
0 |
0.E+000 |
000003 |
REC-3 |
5 |
5 |
5 |
500000000.E-008 |
And there are four logical files—LF_PKD, LF_ZND, LF_BIN, and LF_FLT—based on PF LOVAL01. The logical files are "UNSIGNEDly" keyed by packed decimal field PKD, zoned decimal field ZND, binary field BIN4, and floating-point field FLT4, respectively. So records in the logical files are sequenced by the unsigned binary value of the numeric key fields, like the following:
LF_PKD |
|||
RRN |
CHAR_8 |
PKD_5_0 |
Hex Value of PKD |
000002 |
REC-2 |
0 |
x'00000F' |
000001 |
REC-1 |
5- |
x'00005D' |
If you issue SETLL *LOVAL on LF_PKD to position to the beginning of LF_PKD, since the figurative constant *LOVAL is treated as x'99999D' for a PKD(5,0) field, the file pointer will be positioned to the end of LF_PKD. A READ operation after SETLL *LOVAL will encounter an EOF condition.
LF_ZND |
|||
RRN |
CHAR_8 |
PKD_5_0 |
Hex Value of ZND |
000001 |
REC-1 |
5- |
x'F0F0F0F0D5' |
000002 |
REC-2 |
0 |
x'F0F0F0F0F0' |
Since *LOVAL is treated as x'F9F9F9F9D9' for a ZND(5,0) field, issuing SETLL *LOVAL on LF_ZND will position the file pointer to the end of LF_ZND.
LF_BIN |
|||
RRN |
CHAR_8 |
BIN_4 |
Hex Value of BIN |
000002 |
REC-2 |
0 |
x'00000000' |
000003 |
REC-3 |
5 |
x'00000005' |
Since *LOVAL is treated as x'C4653601' (-999,999,999) for a 9-digit (4-byte) binary field, issuing SETLL *LOVAL on LF_BIN will position the file pointer before record 1, and a following READ operation will retrieve record 1.
LF_FLT |
|||
RRN |
CHAR_8 |
FLT_4 |
Hex Value of FLT |
000002 |
REC-2 |
0 |
x'00000000' |
000003 |
REC-3 |
5 |
x'40A00000' |
Since *LOVAL is treated as x'FF7FFFFF' for a 4-byte floating-point field, issuing SETLL *LOVAL on LF_FLT will position the file pointer to the end of LF_ZND.
As you've seen in all the above conditions, SETLL *LOVAL cannot work as it is supposed to, positioning to the beginning of a file, with all four types of numeric key fields with the UNSIGNED keyword specified. For a packed decimal UNSIGNED key field, since all unsigned binary values of a packed decimal field are less than -999...99 (x'9999...9D') except 999...99 (x'9999...9F'), SETLL *LOVAL will bypass all records except the ones with value -999...99 or 999...99. Similarly, for a zoned decimal UNSIGED key field, a SETLL *LOVAL operation will bypass all records except those whose key value is -999...99 (x'F9F9...D9') or 999...99 (x'F9F9...F9'). For an n-digit binary UNSIGNED key field, a SETLL *LOVAL operation will bypass records whose key values are less than -99...9 (n digits). For a 4-byte or 8-byte floating-point UNSIGNED key field, a SETLL *LOVAL operation will bypass all records except the ones with value -INF or +INF.
SETLL *LOVAL and Numeric Key Fields with the ABSVAL Keyword Specified
ABSVAL is a key field-level keyword to direct the operating system to ignore the sign of the field when it sequences the values associated with this numeric field (in other words, records are sequenced by the absolute value of the numeric key field).
For a keyed access path containing a numeric key field with the ABSVAL keyword specified, the values of the numeric field are treated in the following manner:
1. First, the sign bits of the value of the key field are converted to positive values.
2. Then, the unsigned binary value of conversion is taken to build the access path.
For a zoned decimal value, the higher 4 bits of the right-most byte is set to hex F; for a packed decimal value, the lower 4 bits of the right-most byte is set to hex F; for a binary or floating-point value, the highest bit (sign bit) is cleared.
Similar to a SETLL *LOVAL operation on keyed PF or LF containing a numeric UNSIGNED key field, a SETLL *LOVAL operation on a keyed PF or LF containing a numeric key field with the ABSVAL keyword specified will:
- Bypass all records except the ones with value -99...9 or 99...9 for a packed or zoned decimal key field
- Bypass all records for a binary or floating point key field, since for binary or floating-point, *LOVAL is treated as a negative number (whose highest bit is 1) and the unsigned binary value of a negative number is always greater than that of a positive number or zero.
SETLL *LOVAL and -INF/+INF
In scientific computing, -INF and +INF make sense. For example, adding any number to INF should still be INF, dividing any number by INF should be zero. According to the ILE RPG Reference, when representing a 4-byte or an 8-byte floating-point value, figurative constants *LOVAL and *HIVAL are treated as values, as shown in the following table:
Value of *LOVAL and *HIVAL for Floating-Point |
||
|
4-Byte Floating-Point |
8-Byte Floating-Point |
*LOVAL |
x'FF7FFFFF' |
x'FFEFFFFFFFFFFFFF' |
*HIVAL |
x'7F7FFFFF' |
x'7FEFFFFFFFFFFFFF' |
According to the IEEE standard (IEEE 754), the values of INFs for 4-byte and 8-byte floating-point data are the following:
Value of -INF and +INF for Floating-Point |
||
|
4-Byte Floating-Point |
8-Byte Floating-Point |
-INF |
x'FF800000' |
x'FFF0000000000000' |
+INF |
x'7F800000' |
x'7FF0000000000000' |
Note that although the format of floating-point numbers are implementation-dependent, the floating-point format on IBM i conforms to the IEEE standard. The above-mentioned information can also be found in the documentation on Data Types and Limits on IBM i.
Through a simple RPG example program, you can see that the above-shown INF values can behave correctly when involved in computations, while the RPG figurative constants *LOVAL and *HIVAL cannot be used as positive or negative infinity. Consider the following RPG program, loval_inf.rpgle.
d a s 4f * HUGE d huge s 4f inz(*hival) * INF d ds d c 4a inz(x'7F800000') d inf 4f overlay(c)
/free a = 2 ** 127; // a = 1.701411834605E+038 a /= huge; // a = 5.000000596046E-001 [1]
a = 2 ** 127; a = huge - a; // a = 1.701411631781E+038 [2]
a = 2 ** 127; a /= inf; // a = 0.000000000000E+000 [3]
a = 2 ** 127; a = inf - a; // a = x'7F800000', +INF [4]
*inlr = *on; /end-free |
In the above example, the initial value of 4-byte floating-point variable a is 1.7014118E+038:
[1] Dividing a by *HIVAL is 0.5.
[2] Subtracting a from *HIVAL is 1.7014116E+038.
[3] Dividing a by +INF is zero.
[4] Subtracting a from +INF is still INF.
Since the absolute value of *LOVAL/*HIVAL is great the absolute value of -INF/+INF, a SETLL *LOVAL operation or a SETGT *HIVAL operation against a keyed PF or LF containing a floating-point key field obviously cannot locate records whose floating-point key field values are -INF or +INF, no matter that the floating-point key field has the SIGNED (by default), UNSIGNED, or ABSVAL keyword specified.
*START and *END
According to the "What's New in V4R4?" section in the ILE RPG Language Reference for V5R4, "The new *START and *END values for the SETLL operation code position to the beginning or end of the file." Note that unlike *LOVAL and *HIVAL, which are figurative constants, *START and *END are reserved words of the RPG language.
With the help of *START and *END, you can avoid the problems mentioned at the beginning of this article. Note that when using SETLL *START or SETLL *END, the name operand of SETLL can be only a file name, not a record format name. Assume that the records in a physical file called LOVAL05 that is keyed by a 4-byte floating-point field are like the following:
FLT(4) KEY FIELD A1 000001 0.E+000 +0 000002 -0.E+000 -0 000003 500000000.E-008 +5 000004 -500000000.E-008 -5 000005 *INF +I 000006 *NEGINF -I |
The following RPG program loval06.rpgle can locate record -6 either by a SETLL *START operation followed by a READ operation or by a SETLL -INF operation followed by a READE -INF operation.
fLOVAL05 if e k disk
d ds d a 4a inz(x'FF800000') d neg_inf 4f overlay(a)
/free setll *start LOVAL05; read REC; dsply 'INF REC' '' A1;
setll neg_inf REC; reade neg_inf REC; if not %eof(); dsply 'INF REC' '' A1; endif;
*inlr = *on; /end-free |
Now You Know
With this information in your arsenal, you can now use SETLL *LOVAL with confidence.
as/400, os/400, iseries, system i, i5/os, ibm i, power systems, 6.1, 7.1, V7, V6R1
LATEST COMMENTS
MC Press Online