29
Fri, Nov
0 New Articles

Converting Numeric to Character and Character to Numeric

RPG
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Converting Numeric to Character

Until recently, it had been difficult to convert between numeric and character values in languages other than RPG. In RPG, we simply used the MOVE opcode and numeric-to-character or character-to-numeric conversion was magically done for us.

Today, many languages have built-in functions or implicit conversions between character and numeric. For example, in the C language, the atoi() and itoa() functions convert numeric into character format and character into numeric format, respectively. The advantage of these functions over, say, the MOVE opcode, is that they also do some limited handling of embedded blanks, commas, decimal points, and the so-called status (positive or negative sign). You can't do that with a simple MOVE opcode.

In RPG IV, IBM added two operation codes that would convert numeric values into character values and allow you to mask or edit the resulting values. That way, the character format appears just as it would have, had it been output using RPG IV output specification forms and an edit code or edit word.

The %EDITC (edit code) built-in function's purpose is to convert a numeric value (or the result of a numeric expression) into a character format. Its second parameter is used to control the formatting of the resulting value. It can be any available RPG IV edit code. So, for example, the following code would yield a result of 3,210.50 in TextField:

     C                   Eval      Amtdue = 3210.50
     C                   Eval      TextField = %editc( amtdue : 'J')

One anomaly of the %EDITC built-in function is that the resulting character string that it returns is as long as the original numeric value, plus the edit codes and decimal positions. So if the AMTDUE field used in the example was a nine-position packed field with two decimals, the resulting value would be 12 positions in length, as follows:

 *...v... 1 ...v... 2
'    3,210.50'

A design decision had to be made to either leave in the leading blanks or truncate them. There are good reasons both to leave them in and to take them out. But the decision was made so that when several values are being converted, the resulting character string length would be consistent.

To truncate the leading blanks, wrap the %EDITC built-in function within a %TRIML built-in function, as follows:

%TRIML(%editc( amtdue : 'J')) 

This will eliminate the leading blanks from the result value.

In OS/400 V4R4, IBM introduced the %CHAR built-in function. %CHAR does a fine job of converting numeric values to character. But unlike %EDITC, the %CHAR function strips off leading blanks, and it does not allow you to specify a customized edit code. Edit code L is effectively used by %CHAR.

In the example, %CHAR(amtdue) would return 3210.50. In addition, %CHAR can be used within concatenations. This makes it incredibly easy to create messages intended for humans to read. For example:

     C                   Eval      msg = 'Invoice ' + %Char(InvNbr) + 
     C                                   ' was not found.'

The %CHAR built-in function converts the numeric INVNBR field into character format, stripping off the leading blanks and zeros. So the end user might see something like this:

'Invoice 370120 was not found.'

Converting Character to Numeric

But what about converting character to numeric? Certainly it must be just as easy to do using free-format syntax; after all, the MOVE opcode has been doing it for decades.

What if an end user types 32.50 into a Web page? This value will arrive in your program as character data; literally, '32.50' is sent. But that value needs to be stored in the database as a seven-digit packed field with two decimals. So how do you convert it?

Before you answer, consider this: The end user might enter the value as 32.50 or 1,320.00 or .50 or 0.50 or 1.23- or as simply the number 10. So how can it be converted successfully in all situations?

If you are running OS/400 V5R2, you can use an enhancement to the %DEC built-in function. %DEC (under V5R2) will convert a character value containing valid numeric data into a numeric value. You must specify the resulting field's length and decimal positions, however. Here's an example:

     D AmtDue          S              7P 2
     D TextValue       S             12A   Inz('1,320.50')
     C                   Eval      Amtdue = %Dec(TextValue : 7 : 2)

The first parameter of %DEC is the character string that is converted. The second and third parameters are the length and decimal positions to use for the conversion. All parameters are required.

Alternatively, you can specify the second and third parameters by using the relative size of the result field. To do this, substitute the %LEN and %DECPOS built-in functions in place of the hard-coded length and decimal positions, as follows:

     D AmtDue          S              7P 2
     D TextValue       S             12A   Inz('1,320.50')
     C                   Eval      Amtdue = %Dec(TextValue :
     C                                       %len(amtdue) : %decpos(amtdue))

This may be a bit more complicated and ugly, but it provides more flexibility (read: forgiveness) when you copy the code into another program.

The enhancement to %DEC is also provided for in the %INT built-in function. Both are welcome and long overdue. However, it bothers me that these non-operating-system-specific enhancements are not ported back to V4R5 or even V5R1, thus making it very unlikely that you'll actually use either of them in the next year or more.

So, what do we do about this situation? We implement similar functions ourselves--using existing interfaces--and make it work all the way back to V3R7 and possibly even V3R2.

Do It Yourself

There are two types of conversion from character to numeric.

  • Converting integers or whole numbers
  • Converting numbers with decimal notation, such as dollar values

Converting integers or whole numbers is extremely easy in RPG IV. Simply prototype one of the C language runtime functions and then call it. It's that easy!

Listed in Figure 1 is the RPG IV source for the C language atoi() function. This is just the prototype, but that's all that is required to call the function, except, of course, the ever-present BNDDIR('QC2LE') keyword in the header specification.

.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++++++
0001 D atoi            PR            10I 0 ExtProc('atoi')
0002 D  charValue                      *   VALUE Options(*STRING)

Figure 1: Prototype for atoi (character to integer) function

The function "atoi" actually means "ASCII to integer," but we're not working with an ASCII character set, so we refer to it as "character to integer." In RPG IV, an integer is an "I" data type with a length of 3, 5, 10, or 20 and a decimal position that is always 0.

The length of integer fields in RPG IV is somewhat of a misnomer. Integers are traditionally referred to by their byte count, not the number of digits they hold. So the declared lengths are not the length of the field in bytes. In RPG IV, we have 1-, 2-, 4-, and 8-byte integers, which correspond to the 3, 5, 10, and 20 position "I" fields. For example, a 3i0 field is a 1-byte integer, and a 10i0 field is a 4-byte integer. The table listed in Figure 2 defines each of the integer field lengths and the corresponding data types in RPG IV.

As Declared in RPG
Conventional Identification
Range
3i 0
INT1
-256 through 256
5i 0
INT2
-32 768 through 32 767
10i 0
INT4
-2 147 483 647 through 2 147 483 647
20i 0
INT8
-9 223 372 036 854 775 808 through
9 223 372 036 854 775 807

Figure 2: Integer data types' lengths and ranges

The following illustrates the use of the atoi() procedure in RPG IV.

0001 H BNDDIR('QC2LE')
0002 D InvNbr          S              7P 0
0003 D TextValue       S             12A   Inz('10425')
0004 C                   Eval      InvNbr = atoi(TextValue)

The atoi() function (line 4) converts the textual '10425' into a numeric value and assigns that value to the INVNBR field. Note that assigning the integer value to the packed decimal field is perfectly legal.

There is an obvious problem with atoi(). It handles numbers up to 10 digits in length. This is fine, except it won't work with some telephone numbers or other long numeric values. The IBM ILE C compiler resolves this issue by supporting another function, named atoll(). This function works exactly the same as atoi(), except that it handles much larger values. Up to a 19-digit numeric value may be converted using atoll().

The following illustrates the use of the atoll() procedure in RPGIV.

0001 H BNDDIR('QC2LE')
0002 D Phone           S             13P 0
0003 D TextValue       S             20A   Inz('0118005529402')
0004 C                   Eval      InvNbr = atoll(TextValue)

There is nothing that prevents you from using atoll() exclusively. It works just as well with short integers as it does with long integers (actually they are referred to as "long, long integers," hence the atoll nomenclature). The prototype for atoll() is listed in Figure 3.

.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++++++
0001 D atoll           PR            20I 0 ExtProc('atoll')
0002 D  charValue                      *   VALUE Options(*STRING)

Figure 3: Prototype for the atoll procedure

Remember, when you're using a C language function, you are using a case-sensitive language. So the procedure name "atoll" must be in lowercase when specified as the parameter of the EXTPROC keyword (line 1).

That takes care of integers, but what about currency values--dollars and cents? How do you convert something like "123.45" to a number?

The C language has another function named atof(). This function converts a character string containing numeric data to a floating point value. The atof() (convert ASCII to floating point value) function works pretty well with simple values, but when accuracy is important, atof() can not be relied upon. Figure 4 contains the prototype for the atof() function in RPG IV.

.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++++++
0001 D atof            PR             8F   ExtProc('atof')
0002 D  charValue                      *   VALUE Options(*STRING)

Figure 4: Prototype for the atof procedure

Instead of atof(), there are other interfaces that can accomplish a similar, yet more accurate, result. The AS/400 and iSeries have something that is at a lower level than the C language, and that is the Machine Interface (MI). In effect, this is OS/400 Assembly language. While I don't want to get into the virtues of MI programming, I do advocate using a bit of MI now and then, especially today, since you can call an MI instruction directly from within RPG IV.

The MI instruction CVTEFN (Convert external form to numeric) allows you to convert from character to numeric. The resulting value can be virtually any numeric data type and length. The documentation on CVTEFN states the following:
"Scans a character source for a valid decimal number in display format, removes the display character, and places the result in receiver."

The syntax diagram for CVTEFN is as follows:

void _CVTEFN (_SPCPTR receiver,
_DPA_Template_T *rcvr_descr,
_SPCPTRCN source,
unsigned int *src_length,
_SPCPTR mask);

This seems complex, but it isn't. It just used different names for parameters. For example, _SPCPTR is a pointer to a variable. In RPG IV, that means it's a regular parameter. To create a parameter list to call CVTEFN, the RPG IV code in Figure 5 could be used.

.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++++++
0001 D DPA_Template    DS 
0002 D  DPA_type                      1A   Inz(T_PACKED) 
0003 D  DPA_len                       5I 0 
0004 D    DPA_decpos                  3I 0 overlay(DPA_len:1) Inz(9)  
0005 D    DPA_length                  3I 0 overlay(DPA_len:2) Inz(30) 
0006 D  DPA_res                      10I 0 Inz(0) 

      ** The prototype for the CVTEFN MI function, used by the
      ** CharToNum procedure to do the conversion.
.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++++++
0007 D CvtEFN          PR                  ExtProc('_CVTEFN')
0008 D  pRtnValue                      *   VALUE
0009 D  DPA_Templ                          Const LIKE(DPA_Template)
0010 D  CharValue                   256A   Const
0011 D  nCharValueLen                10U 0 Const
0012 D  EditMask                      3A   Const

Figure 5: Prototype to call CVTEFN

In Figure 5, lines 1 through 6 contain a data structure that is used as the second parameter for the call to CVTEFN. Since data structures cannot be directly declared on parameter lists, we need to declare the data structure separately. If you are running OS/400 V5R1 or later, the LIKE keyword used on line 6 may be replaced with the LIKEDS keyword.

Lines 7 through 12 illustrate the procedure prototype/parameter list in RPG IV for a call to CVTEFN. We are actually calling '_CVTEFN', which is a little different from the other CVTEFN MI instructions. The leading underscore indicates that this function is an in-line function; its code is actually inserted into the module we are creating, thus providing faster processing since the overhead of an additional procedure call is avoided. Just how much it saves us in RPG IV is subjective.

I've create a wrapper procedure to call _CVTEFN that makes using the MI function as easy as calling the atoi() function.

Listed in Figure 6 is the source code for an RPG IV module that can be compiled and used with your existing programs. It can be compiled as a *MODULE object and bound by copy, or the module can be linked into a *SRVPGM (service program) and bound by reference.

The syntax to call this wrapper procedure, named CHARTONUM, is as follows:

numeric-value  =  CharToNum( 'char-string' [ : 'edit-mask' ] )

The CharToNum procedure accepts a character string input field or literal value and returns a packed decimal return value.

The first parameter may be any valid character string that contains a numeric value. The character string may contain an edited form of a number or a simple numeric value. Blanks, commas, periods, currency symbols, and a status (positive or negative indication) may be embedded in the string.

The second parameter is an optional edit mask, and it may contain the symbols used for currency symbol, thousands separator, or decimal notation. If unspecified, the default mask is '$,.'. To change this default, modify the DFTEDITMASK named constant on line 23 in Figure 6.

0001 H NOMAIN BNDDIR('QC2LE'

      ** Here is the prototype for CHARTONUM
      ** Cut/paste this into another source member
      ** then /COPY it into this source member and into
      ** any other source member that calls CHARTONUM.

      ** ------------ START OF PROTOTYPE
0002 D CharToNum       PR            30P 9
0003 D  InString                    256A   Const VARYING 
0004 D  Format                        3A   Const options(*NOPASS)
      ** ------------ END OF PROTOTYPE

      ** T_PACKED is a hex value that tells CVTEFN to convert to 
      ** a packed data type. The length and decimal positions of
      ** the resulting value be specified dynamically, however
      ** we use 30 with 9 decimals since we're assigned the result
      ** to our own field with presumably the proper field length.
.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++++++
0005 D T_PACKED        C                   X'03' 
      ** DPA_TEMPLATE is the structure used by CVTEFN to do 
      ** the conversion. It contains a description of the format
      ** for the numeric value to be returned to the caller.
      ** So the first parm of CVTEFN must be the same format as
      ** described in this DPA_TEMPLATE. It is initialized here
      ** to the necessary values for our usage in this example.
.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++++++
0006 D DPA_Template    DS 
0007 D  DPA_type                      1A   Inz(T_PACKED) 
0008 D  DPA_len                       5I 0 
0009 D    DPA_decpos                  3I 0 overlay(DPA_len:1) Inz(9)  
0010 D    DPA_length                  3I 0 overlay(DPA_len:2) Inz(30) 
0011 D  DPA_res                      10I 0 Inz(0) 

      ** The prototype for the CVTEFN MI function, used by the
      ** CharToNum procedure to do the conversion.
.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++++++
0012 D CvtEFN          PR                  ExtProc('_CVTEFN')
0013 D  pRtnValue                      *   VALUE
0014 D  DPA_Templ                          Const LIKE(DPA_Template)
0015 D  CharValue                   256A   Const
0016 D  nCharValueLen                10U 0 Const
0017 D  EditMask                      3A   Const

.....PName+++++++++++..B...................Keywords++++++++++++++
0018 P CharToNum       B                   Export

      ** CHARTONUM - Convert Character string to numeric Pkd(30,9)
      **   usage:   eval  myNumValue = CharToNum('$3,741.63')
      **            eval  InvNbr = CharToNum( INVOICE )
      **              // where INVOICE is a character field.
      **   Parms:
      **     InString - Input character value containing a numeric
      **                string in character form. Any length value
      **                up to 256 positions may be specified.
      **                Leading and trailing blanks are trimmed off.
      **     Format   - Input, optional. Used to override the default
      **                settings for the Currency symbol, thousands
      **                separator, and decimal notation. If not 
      **                specificied, the values '$,.' are used.
.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++++++
0019 D CharToNum       PI            30P 9
0020 D  InString                    256A   Const VARYING 
0021 D  Format                        3A   Const Options(*NOPASS)

.....DName+++++++++++EUDS.......Length+TDc.Functions+++++++++++++
      ** Return value
0022 D NumValue        S             30P 9

      ** Default Currency, thousands, decimal notation  '$,.'
0023 D dftEditMask     C                   '$,.' 
0024 D szEditMask      S              3A   Inz(dftEditMask) 

.....CSRn01..............OpCode(ex)Extended-factor2++++++++++++++
0025 C                   if        %Parms >= 2
0026 C                   eval      szEditWord = Format
0027 C                   endif

0028 C                   callp     cvtefn( %addr(NumValue)
0029 C                                   : DPA_Template 
0030 C                                   : %trim(InString)
0031 C                                   : %len(%trim(InString))
0032 C                                   : szEditMask ) 
0033 C                   return    NumValue
0034 P CharToNum       E

Figure 6: Source module for the CHARTONUM procedure

I tested this procedure using the following eight test values:
D szTest1 S 20A Inz('3,741.52')
D szTest2 S 20A Inz('$3,741.52')
D szTest3 S 20A Inz('3,741.52-')
D szTest4 S 20A Inz(' 3741')
D szTest5 S 20A Inz(' -3,741.52')
D szTest6 S 20A Inz('$-3,741.52')
D szTest7 S 20A Inz(' $3,741.52-')
D szTest8 S 20A Inz('-$3,741.52')

The first seven tests converted to numeric as expected. However, the eighth test failed. Apparently CVTEFN does not like the negative sign in front of the currency symbol. Also, in other testing, phone number or social security number editing sequences failed when using CVTEFN, so we're limited to integers and currency values.

The bottom line is CharToDec is a great asset to your RPG programming tool kit. It easily converts character strings containing integers and decimal values to numeric without much trouble.

BOB COZZI

Bob Cozzi is a programmer/consultant, writer/author, and software developer. His popular RPG xTools add-on subprocedure library for RPG IV is fast becoming a standard with RPG developers. His book The Modern RPG Language has been the most widely used RPG programming book for more than a decade. He, along with others, speaks at and produces the highly popular RPG World conference for RPG programmers.


MC Press books written by Robert Cozzi available now on the MC Press Bookstore.

RPG TnT RPG TnT
Get this jam-packed resource of quick, easy-to-implement RPG tips!
List Price $65.00

Now On Sale

The Modern RPG IV Language The Modern RPG IV Language
Cozzi on everything RPG! What more could you want?
List Price $99.95

Now On Sale

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: