
As part of the latest Technology Refreshes, IBM i 7.5 TR5 and 7.4 TR11, two new RPG built in functions, BiF, were added: %HIVAL and %LOVAL.
Their names describe what they return:
- %HIVAL: Returns the highest value that can be returned from the variable
- %LOVAL: Returns the lowest value that can be returned from the variable
The syntax for the BiF is:
%hival(variable) %loval(variable) |
The parameter can either be a variable or it can be an enumeration group name.
I can see using this to flag that a numeric variable is going to "overflow". Below is an example of some code where the result of the calculation "overflows" and errors:
01 **free 02 dcl-s InvoiceTotal packed(11:2) inz(*all'9') ; 03 dcl-s LineItem packed(7:2) inz(1) ; 04 InvoiceTotal += LineItem ; |
Line 2: Here I have defined InvoiceTotal, and initialized it with all 9s.
Line 3: This variable contains the value that will be added to InvoiceTotal to cause the "overflow".
Line 4: Perform the calculation that "overflows", and errors.
Message id: RNQ0103 The target for a numeric operation is too small to hold the result (C G D F). |
There are various ways I code to stop that happening. Using the new %HIVAL it becomes so much simpler. I would replace line 4 with the following:
04 if ((InvoiceTotal + LineItem) > %hival(InvoiceTotal)) ; 05 snd-msg *escape 'Variable InvoiceTotal not big enough for ' + %char(InvoiceTotal + LineItem) ; 06 endif ; |
Line 4: I am comparing the total of InvoiceTotal and LineItem to the highest possible value for InvoiceTotal. If the total is greater then the rest of the If group is performed.
Line 5: I am using the SND_MSG operation code to write to the joblog and generate an escape type error:
Message id: CEE9901 Application error. CPF9898 unmonitored by TESTRPG at statement *N, instruction X'0000'. |
And in the joblog I see:
Variable InvoiceTotal not big enough for 1000000000.99. |
If I wanted to handle this situation within my code, without an escape message, I would modify the code to be:
04 if ((InvoiceTotal + LineItem) > %hival(InvoiceTotal)) ; 05 snd-msg 'Variable InvoiceTotal not big enough for ' + %char(InvoiceTotal + LineItem) ; 06 dsply ('InvoiceTotal too small for ' + %char(InvoiceTotal + LineItem)) ; 07 endif ; |
Line 4: Is unchanged.
Line 5: I have removed the "*ESCAPE" as I want this message to be written to the job log with being an escape error message.
Line 6: I have added the Display operation code for testing purposes.
Now when the program is called I see:
DSPLY InvoiceTotal too small for 1000000000.99 |
And in the job log I see:
Variable InvoiceTotal not big enough for 1000000000.99 DSPLY InvoiceTotal too small for 1000000000.99 |
For an example of using the %LOVAL I decided to use an unsigned integer variable to demonstrate:
01 **free 02 dcl-s UnsignedInteger uns(3) ; 03 dcl-s Integer int(3) inz(-1) ; 04 UnsignedInteger += Integer ; |
Line 2: This is the definition of the unsigned integer variable. As I did not initialize it with a value it will contain zero at the start of the program.
Line 3: This variable contains the value that will be added to the unsigned integer, this is initialized with -1.
Line 4: This calculation errors as I cannot add -1 to zero as the result is less than zero, which is not valid for an unsigned integer, and the following error message text is displayed:
The target for a numeric operation is too small to hold the result (C G D F) |
I use the %LOVAL BiF in the following manner to prevent the error from happening:
03 if ((UnsignedInteger + Integer) < %loval(UnsignedInteger)) ; 04 dsply ('UnsignedInteger too small for ' + %char(UnsignedInteger + Integer)) ; 05 endif ; |
Line 3: If the total of UnsignedInteger and Integer is less than the lowest allowed value for UnsignedInteger then the If group is performed.
Line 4: As this is a test/demonstration program I am using the DSPLY op-code. In a production program I would use the SND-MSG.
When I run this program the following is shown:
DSPLY UnsignedInteger too small for -1 |
I mentioned at the start of this post I can use %HIVAL and %LOVAL with an enumeration group. This is an example of how it can be used:
01 **free 02 dcl-enum EnumGroup qualified ; 03 first 423 ; 04 second 993 ; 05 third 595 ; 05 fourth 323 ; 06 end-enum ; 07 dsply ('Hival ' + %char(%hival(EnumGroup))) ; 08 dsply ('Loval ' + %char(%loval(EnumGroup))) ; 09 *inlr = *on ; |
Lines 2 - 6: My enumeration group has four subfields, each with its own various random number.
Line 7: I am using the DSPLY op-code to display the %HIVAL for the group.
Line 8: I am doing the opposite, displaying the %LOVAL for the group.
When the program is called I see the highest and lowest numbers in the group:
DSPLY Hival 993 DSPLY Loval 323 |
My last example shows how I can use these BiF to display the highest and lowest possible values from various variable types.
01 **free 02 dcl-s Uns1 uns(3) ; 03 dcl-s Uns2 uns(5) ; 04 dcl-s Uns3 uns(10) ; 05 dcl-s Int1 int(3) ; 06 dcl-s Int2 int(5) ; 07 dcl-s Int3 int(10) ; 08 dcl-s Date1 date(*usa) ; 09 dcl-s Date2 date(*mdy) ; 10 dsply ('Hival for Uns1 ' + %char(%hival(Uns1))) ; 11 dsply ('Loval for Uns1 ' + %char(%loval(Uns1))) ; 12 dsply ('Hival for Uns2 ' + %editc(%hival(Uns2) : 'J')) ; 13 dsply ('Loval for Uns2 ' + %char(%loval(Uns2))) ; 14 dsply ('Hival for Uns3 ' + %editc(%hival(Uns3) : 'J')) ; 15 dsply ('Loval for Uns3 ' + %char(%loval(Uns3))) ; 16 dsply ('Hival for Int1 ' + %char(%hival(Int1))) ; 17 dsply ('Loval for Int1 ' + %char(%loval(Int1))) ; 18 dsply ('Hival for Int2 ' + %editc(%hival(Int2) : 'J')) ; 19 dsply ('Loval for Int2 ' + %editc(%loval(Int2) : 'J')) ; 20 dsply ('Hival for Int3 ' + %editc(%hival(Int3) : 'J')) ; 21 dsply ('Loval for Int3 ' + %editc(%loval(Int3) : 'J')) ; 22 dsply ('Hival for Date1 ' + %char(%hival(Date1))) ; 23 dsply ('Loval for Date1 ' + %char(%loval(Date1))) ; 24 dsply ('Hival for Date2 ' + %char(%hival(Date2))) ; 25 dsply ('Loval for Date2 ' + %char(%loval(Date2))) ; 26 *inlr = *on ; |
Lines 2 - 4: Defining unsigned integer variables of various sizes.
Lines 5 - 7: Defining integer variables of various sizes.
Lines 8 and 9: A couple of date variables too.
Lines 10 - 25: I am using the DSPLY op-code to display the values returned by %HIVAL and %LOVAL. On some lines I am using the %EDITC BiF as I want thousand separator characters.
The results displayed for the unsigned integers were:
DSPLY Hival for Uns1 255 DSPLY Loval for Uns1 0 DSPLY Hival for Uns2 65,535 DSPLY Loval for Uns2 0 DSPLY Hival for Uns3 4,294,967,295 DSPLY Loval for Uns3 0 |
Results for the integers:
DSPLY Hival for Int1 127 DSPLY Loval for Int1 -128 DSPLY Hival for Int2 32,767 DSPLY Loval for Int2 32,768- DSPLY Hival for Int3 2,147,483,647 DSPLY Loval for Int3 2,147,483,648- |
And the dates:
DSPLY Hival for Date1 9999-12-31 DSPLY Loval for Date1 0001-01-01 DSPLY Hival for Date2 2039-12-31 DSPLY Loval for Date2 1940-01-01 |
I will be incorporating this BiF into my work as I think using them is easier to code and for some to understand how I can check if a variable will "overflow".
You can learn more about the %HIVAL and %LOVAL BiF from the IBM website here.
This article was written for IBM i 7.5 TR5 and 7.4 TR11.
thanks. Pretty curious the difference between the two dates variables, I would have though both equal in hi lowval, based on the internal DATE type capability and not their string format representation.
ReplyDelete