Normal Topic A Better Money to Words Converter (Read 4706 times)
jfreeman
Member
*
Offline


No personal text

Posts: 23
Location: KZN, South Africa
Joined: Apr 10th, 2005
A Better Money to Words Converter
Jul 20th, 2013 at 5:06pm
Print Post Print Post  
/*##################################################
MONEY VALUE TO WORDS CONVERTER (Correct English, grammar and capitalization)

For amounts less than one quadrillion (inflation is no problem!), easily extendable by adding to
the array 'Triplet' e.g. Triplet[7] = "quadrillion".
It is independent of the decimal separator symbol used, assumes the fractional currency unit
is a hundredth of the main currency.
Converts an amount like R3025,55 to "Three thousand and twenty-five rands and fifty-five cents".
Requires a Money LE named "Amount" and Text LE's named "AmtWords1", "AmtWords2" and 'AmtWords3",
to fill the three lines usually present on cheque(=check, US) forms.

Can print cheques directly from Sesame 2 via a Word Merge.

The amount to be converted is in an LE named 'Amount'

There are three user selectable variables: -

vCurrency holds the name of the currency unit, e.g. 'dollars' 'pounds' 'euros' or 'rands'
vWordlength is the capacity in characters of a word description line on the users's cheque (set here to 36).
Triplet[1] holds the name of the fractional currency unit, in this case, 'cents.'

#################################################### */

var vCurrency as string = "rands"
var vWordLength as int = 36


var vAmount as String
var vWords as String = ""
var vRands as String = ""
var vCents as String = ""
var vChunk as String
var vHundreds as Int
var vTens as Int
var vUnits as Int
var vTemp as int
var vChunkL as string
var vChunkR as string
var Tens as array[9] of string
var U20 as array[19] of string
var Triplet as array[6] of string
var vHold as string
var n as int
var vloop as int
var vCnt as int
var vx as Boolean
var vy as Boolean
var vz as Boolean
var vq as Boolean

Subroutine ParseChunk()

/* This accepts a three character portion of the vAmount string (which is always 3(n +1) characters long),
and converts it to words in the form 'seven hundred and seventy-seven.'
*/

if vChunk <> "000"                                          //There is a real amount to be converted from the chunk                  
{
vChunkL = ""                                                //Initialise
vChunkR = ""                                                //Initialise
vHundreds = @tn(@lt(vChunk, 1))                                    //Separate the hundreds digit
if vHundreds > 0                                          //Hundreds digit is not zero
     {
     vChunkL = U20[vHundreds]                              //Words description of the hundreds digit
     }
vTemp = @tn(@Rt(vChunk, 2))                                    //Numeric value of the tens and units of the chunk
If vTemp <> 0                                                //Numeric value of tens and units is not zero
     {
     if vTemp > 19                                          //Numeric value of tens and units is greater than 19
           {
           vTens = @tn(@Mid(vChunk, 2, 1))                        //Split: this is the tens digit
           vUnits = @tn(@rt(vChunk, 1))                        //Split: this is the units digit
           vChunkR = Tens[vTens]                              //Word description of the tens digit, by selection non-zero
           If vUnits > 0                                    //The units digit is not zero
                 {
                 vChunkR = vChunkR + "-" + U20[vUnits]            //Assemble the string representing the tens and units digits
                 }      
           }
           Else                                          //Numeric value of rightmost 2 characters (tens and units) is less than 19
           {
           vChunkR = U20[vTemp]                              //Convert both tens and untis to words at once
           }
     }
/*
Assemble the output string. Since there may be no hundreds, vChunkL may be an empty string.  Likewise there may be no tens or no units.
The words 'hundred' & 'and' are therefore added condtionally, depending on which digits in the chunk have value above zero,
and the position of the chunk in the vAmount string, 
Finally, the correct description of the whole chunk is added via the selection from the Triplet array.
*/

vx = vChunkL <> "" and vChunkR <> ""                              //'And' is required when both hundreds and tens or tens and units, or units alone, are present
vy = n + 1 > vloop and vloop = 1 and vChunk <> "000"                  //'And' is required between vCurrency and cents strings when neither is an empty string
vz = n + 1 > vloop and vLoop = 2 and vChunkL = "" and vChunkR <> ""      //'And' is required here, and only here, by a peculiarity of the English language!
vq = n + 1 <> vloop or vChunkL <> ""                              //'And' is excluded at the beginning of the vWords string.

vWords = vWords + @txt(vChunkL <> "", " ") + vChunkL + @txt(vChunkL <> "", " hundred") + @txt((vx + vy + vz)*vq, " and ") + vChunkR + " " + Triplet[vloop]

}
Else                                                      //Chunk is equal to "000"
{
if vloop = 1                                                //Chunk being evaluated is the cents
     {
     vWords = vWords + " only."                              //The word 'only' is added to prevent unauthorised alteration of the string if there are no cents
     }
if vloop = 2                                                //Chunk being evaluated is the hundreds, tens and units of vCurrency, but its value is "000"
     {
     if vWords = ""                                          //There are no earlier (= leftmost) Chunks containing nonzero data
           {
           vWords = "Zero " + Triplet[vloop]                  //Cheque reads  'Zero currency units'
           }
           Else                                          //There are nonzero data in chunks whose designator is 'thousands' or higher.
           {
           vWords = vWords + " " + Triplet[vloop]                  //Cheque correctly inserts vCurrency even though the chunk being reported on is zero.
           }                                          //If some idiot does write a null cheque, it will correctly read 'Zero currency units only.'
     }
}
End Subroutine

//MAIN PROGRAMME

// Fill the three arrays.

     Tens[1] = "Ten"
     Tens[2] = "Twenty"
     Tens[3] = "Thirty"
     Tens[4] = "Forty"
     Tens[5] = "Fifty"
     Tens[6] = "Sixty"
     Tens[7] = "Seventy"
     Tens[8] = "Eighty"
     Tens[9] = "Ninety"
     U20[1] = "One"
     U20[2] = "Two"
     U20[3] = "Three"
     U20[4] = "Four"
     U20[5] = "Five"
     U20[6] = "Six"
     U20[7] = "Seven"
     U20[8] = "Eight"
     U20[9] = "Nine"
     U20[10] = "Ten"
     U20[11] = "Eleven"
     U20[12] = "Twelve"
     U20[13] = "Thirteen"
     U20[14] = "Fourteen"
     U20[15] = "Fifteen"
     U20[16] = "Sixteen"
     U20[17] = "Seventeen"
     U20[18] = "Eighteen"
     U20[19] = "Nineteen"
     Triplet[1] = "cents"      //This is user selectable to match the fractional currency unit name
     Triplet[2] = vCurrency
     Triplet[3] = "thousand"
     Triplet[4] = "million"
     Triplet[5] = "billion"
     Triplet[6] = "trillion"

/*Section 1 - Configure vAmount.

vAmount is the string that represents the input Amount. The aim here: convert the digits of the input number to a string 3(n + 1)
characters long, where n is an integer (here from from 0 to 5). The decimal symbol is replaced by a zero,
since its position is known (the three rightmost string characters represent respectively the decimal symbol followed by the cents).
This is done to ensure that when the resulting string is split into 3-character chunks, there will never be a remainder,
allowing the whole string to be processed by the ParseChunk subroutine.
*/

if Amount <> 0                                                      //Input from the LE
{
if @frac(Amount)*100 > 0                                          //There are cents to be converted
     {
     vCents = @str(@Round(@frac(Amount)*100, 0))                        //Round cents to remove fractions and convert to string
     vCents = @txt(3 - @len(vCents), "0") + vCents                        //Adjust the vCents string to exactly 3 characters length, of which
     }                                                      //the  last two are the significant figures and the first is the
     Else                                                      //decimal symbol
     {
     vCents = "000"                                                //There are no cents to be converted
     }
if @int(Amount) > 0
     {
     vRands = @str(@int(Amount))
     n = @int(@len(vRands)/3) + 1*(@frac(@len(vRands)/3) <> 0)            //This formula results in vRands having a length of a 3n characters
     vRands = @txt(3*n - @len(vRands), "0") +vRands                        //filled out with just enough leading zeroes. e.g 2 characters becomes 3,
     }                                                      //3 characters remains 3, 4 becomes 6, and so on.
vAmount = vRands + vCents                                          //A string 3(n + 1) digits long in which the decimal symbol is replaced by a zero.
}

/*Section 2

Put the vAmount string through the ParseChunk subroutine n times, incrementing the 'start position' of the chunk
by three places rightward at each pass.  At each pass, vWords is added to by the subroutine until the full
expression is built up.
*/

vloop = n + 1
vCnt = 1
While vloop >= 1
{
vChunk = @mid(vAmount, vCnt, 3)                                          //Take a 3 character chunk
ParseChunk()                                                      //Parse it into words and add it to the previous result
vloop = vloop - 1                                                //Decrement the loop counter
vCnt = vCnt + 3                                                      //Move the 'chunk selector' rightwards by 3 characters
}



/*Section 3

Adjust the expression to eliminate unwanted capital letters (of necessity the two arrays U20 and Tens include them).
Parse the vWords string into three portions, each having whole words only and <= vWordLength characters.
*/

if @lt(vWords, 1) = " "
{
vWords = @rt(vWords, @len(vWords) -1)                                                //Prune any leading space
}
vWords = ToLower(vWords)                              
vWords = ToUpper(@lt(vWords, 1)) + @rt(vWords, @len(vWords) - 1)

if @len(vWords) >= vWordLength                                                      
{
vHold = @repllas(@lt(vWords, vWordLength + 1), " ", "*")                              //Take first vWordLength + 1 characters, replace last space in the section with "*"
AmtWords1 = @lt(vHold, @instr(vHold, "*") -1)                                          //Set AmtWords1 = the string before the "*"                                                
vWords = @rt(vHold, @len(vHold) - @instr(vHold, "*")) + @rt(vWords, @len(vWords) -37)            //Reassemble the vWords string, stripped of AmtWords1 and the "*"
if @len(vWords) >= 36                                                            //If the remaining vWord string is still vWordLength or more characters long
     {
     vHold = @repllas(@lt(vWords, vWordLength + 1), " ", "*")                        //Again take first vWordLength + 1 characters, replace last space in the section with "*"
AmtWords2 = @lt(vHold, @instr(vHold, "*") -1)                                          //Set AmtWords2 = the vHold string before the "*"
     vWords = @rt(vHold, @len(vHold) - @instr(vHold,"*")) + @rt(vWords, @len(vWords) -37)      //Reassemble the vWords string, stripped of AmtWords2 and the "*"      
     AmtWords3 = vWords                                                      //This is AmtWotrds3
     }
     else                                                                  //The remaining string after the first truncation was less than vWordLength characters long
     {
     AmtWords2 = vWords
     }
}
else                                                                        //The original vWords string was less than vWordLength characters long
{
AmtWords1 = vWords
}

Ends.  I acknowledge my use of ideas from the Sesame Programming Manual, whose limitations inspired this.

John F

  

John F
Back to top
 
IP Logged