Wednesday, January 15, 2025

New built in functions %HIVAL and %LOVAL

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.

1 comment:

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

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.