Several years ago, we received many requests for a routine to convert a number to words. In response, we published an RPG III program that handled eight-digit numbers of two decimal positions. (See "TechTalk: Numbers-to-Words Conversion Program," MC, May 1994.)
This month, we present an RPG IV service program that contains two nifty subprocedures. The first will spell out any whole number between zero and 4,294,967,295, according to United States conventions. (That is, 10 to the 9th power is called one billion, not one thousand million.) We call it SpellNumber.
The second is a specialized application of SpellNumber, designed for writing amounts of money. We call it SpellCurrency.
If you don't have a routine like this or if you're using an old OPM routine, here's something to add to your ILE toolbox.
This utility consists of two source members. One is a copybook member called NBR01CPY. You can see it in Figure 1. It contains the procedure prototype for subprocedures SpellNumber and SpellCurrency. Any programs or subprograms that use one of these subprocedures should include a /COPY compiler directive for this member.
The second source member, shown in Figure 2, contains the source for service program NBR01SRV. Let's look at this service program and see how it works.
The service program consists of a main section, four subprocedures, and data for the compile- time arrays. The main section declares the global compile-time array variables the subprocedures need and declares the prototypes for the first two subprocedures.
The first two subprocedures-DivideUnsigned and Convert-Units-can be used only within this service program, because they do not include the EXPORT keyword on their procedure definitions. Only the last two subprocedures, SpellNumber and SpellCurrency, may be referred
to by procedures outside this service program. If you want to make the DivideUnsigned or the ConvertUnits subprocedure available to outside procedures, move its prototype into the NBR01CPY source member and add the EXPORT keyword to the subprocedure definition.
DivideUnsigned provides a way to split an unsigned integer into a quotient and remainder of division. I use it to split off the digits and groups of digits for processing. Nothing in this service program will ever divide by zero, but I allowed for that possibility anyway (you never know- I may wind up using this routine somewhere else someday). I chose to return a quotient of zero and a remainder of zero in those cases, rather than set up an error-handling mechanism.
ConvertUnits converts an integer between 1 and 999 to words. Its purpose is to handle each group of three digits. SpellNumber invokes ConvertUnits up to four times, each time passing it another group of three digits, beginning with the rightmost group. As each group of three digits is converted to words, SpellNumber appends the denomination of the group (thousands, millions, etc.) and separates the text description of each group with commas.
SpellCurrency is provided as an easy way to print a dollar amount. It calls SpellNumber twice- once to spell the number of dollars and once to spell the number of cents.
I've included a short test program in Figure 3. In a real application, the UnsNumber and CheckAmount variables would come from a file or be calculated, and the AmountInWords variable would be written to a file. If you run this short test program, use the interactive debugger to see the results, a sample of which are printed for you in Figure 4.
Feel free to modify these subprocedures to fit your needs. For example, SpellNumber returns a string in all uppercase letters. That's probably OK for printing checks, but you may want another case for other applications. I suggest you change the compile-time array data to the case you think you'll need most often. When you need the string to be in some other case, use SpellNumber as an argument to the case conversion functions we presented in the January 1997 "RPG Building Blocks" column.
Also, you may prefer to make SpellCurrency print a numeral, rather than words, for the cents portion of a dollar amount.
Ted Holt is a technical editor with Midrange Computing. You can reach him by email at holt@ midrangecomputing.com.
ILE RPG/400 Reference (SC09-2077, CD-ROM QBJAQE01)
Figure 1: Source member NBR01CPY contains subprocedure prototypes
D SpellNumber pr 128a
D UnsNumber 10u 0 value
D SpellCurrency pr 128a
D CurrencyAmt 9p 2 value *===============================================================
* Spell an unsigned number.
*
* To compile:
Figure 2: Service program NBR01SRV
*
* CRTRPGMOD MODULE(XXX/NBR01SRV) SRCFILE(XXX/QRPGLESRC)
*
* CRTSRVPGM SRVPGM(XXX/NBR01SRV) SRCFILE(XXX/QRPGLESRC) +
* EXPORT(*ALL)
*
*===============================================================
H NoMain
/copy xxx/qrpglesrc,Nbr01Cpy
D DivideUnsigned pr
D Dividend 10u 0 value
D Divisor 10u 0 value
D Quotient 10u 0
D Remainder 10u 0
D ConvertUnits pr 128a
D Units 10u 0 value
D ListOfUnits s 9 dim(19) CTData PerRcd(7)
D ListOfTens s 7 dim( 9) CTData PerRcd(9)
D Group s 8 dim( 4) CTData PerRcd(4)
*===============================================================
* Divide, returning quotient and remainder
*===============================================================
P DivideUnsigned B
Dpi
D Dividend 10u 0 value
D Divisor 10u 0 value
D Quotient 10u 0
D Remainder 10u 0
C if Divisor <> *zero
C Dividend div Divisor Quotient
C mvr Remainder
C else
C eval Quotient = *zero
C eval Remainder = *zero
C endif
PE
*===============================================================
* Convert a number between 1 and 999 to words
*===============================================================
P ConvertUnits B
Dpi 128a
D Units 10u 0 value
D Hundreds s 10u 0
D TensAndOnes s 10u 0
D Tens s 10u 0
D Ones s 10u 0
D WorkString s 128a
C eval WorkString = *blanks
C callp DivideUnsigned (Units: 100:
C Hundreds: TensAndOnes)
C
C if TensAndOnes *zero
C if TensAndOnes <= 19
C eval WorkString = ListOfUnits (TensAndOnes)
C else
C callp DivideUnsigned (TensAndOnes: 10:
C Tens: Ones)
C eval WorkString = ListOfTens (Tens)
C if Ones *zero
C eval WorkString = %trim(WorkString) + '-' +
C ListOfUnits (Ones)
C endif
C endif
C endif
C
C if Hundreds *zero
C eval WorkString = %trim(ListOfUnits (Hundreds))
C +'HUNDRED'+%trim(WorkString)
C endif
C
C Return WorkString
PE
*===============================================================
* Convert a number between 0 and 4,294,967,295 to words
*===============================================================
P SpellNumber B Export
Dpi 128a
D UnsNumber 10u 0 value
D ixs10u 0
D Units s 10u 0
D WorkNumber s 10u 0
D WorkString s 128a
D UnitsString s 128a
C if UnsNumber *zero
C eval WorkString = *blanks
C eval WorkNumber = UnsNumber
C eval ix = 1
C dow WorkNumber *zero
C callp DivideUnsigned (WorkNumber: 1000:
C WorkNumber: Units)
C if Units *zero
C eval UnitsString = %trim(ConvertUnits (Units))
C +''+Group(ix)
C if WorkString = *blanks
C eval WorkString = UnitsString
C else
C eval WorkString = %trim(UnitsString) +
C ','+WorkString
C endif
C endif
C eval ix=ix+1
C enddo
C else
C eval WorkString = 'ZERO'
C endif
C
C return WorkString
PE
*===============================================================
* Convert an amount of currency to words
*===============================================================
P SpellCurrency B Export
Dpi 128a
D CurrencyAmt 9p 2 value
D Dollars s 7p 0
D Cents s 2p 0
C if CurrencyAmt < *zero
C mllzo '0' CurrencyAmt
C endif
C movel CurrencyAmt Dollars
C move CurrencyAmt Cents
C return %trim(SpellNumber(Dollars)) +
C ' DOLLARS AND ' +
C %trim(SpellNumber(Cents)) +
C ' CENTS'
PE
**CTData ListOfUnits
ONE TWO THREE FOUR FIVE SIX SEVEN
EIGHT NINE TEN ELEVEN TWELVE THIRTEEN FOURTEEN
FIFTEEN SIXTEEN SEVENTEENEIGHTEEN NINETEEN
**CTData ListOfTens
TEN TWENTY THIRTY FORTY FIFTY SIXTY SEVENTYEIGHTY NINETY
**CTData Group
THOUSANDMILLION BILLION *===============================================================
* Test the SpellNumber and SpellCurrency functions.
*===============================================================
* To compile:
*
* CRTRPGMOD MODULE(XXX/NBR01TST) SRCFILE(XXX/QRPGLESRC)
*
* CRTPGM PGM(XXX/NBR01TST) BNDSRVPGM(XXX/NBR01SRV)
*
*===============================================================
/copy xxx/qrpglesrc,Nbr01Cpy
D AmountInWords s 128a
D CheckAmt s 9p 2 inz(2408719.57)
D UnsNumber s 10u 0 inz(1788818317)
C eval AmountInWords = SpellNumber(UnsNumber)
C eval AmountInWords = SpellCurrency(CheckAmt)
C eval *inlr = *on TWO MILLION, FOUR HUNDRED EIGHT THOUSAND, SEVEN HUNDRED NINETEEN DOLLARS AND FIFTY-SEVEN CENTS
Figure 3: Use this RPG program to test the SpellNumber
and SpellCurrency functions
and SpellCurrency functions
LATEST COMMENTS
MC Press Online