开发者

Algorithm that converts numeric amount into English words

What is the most efficient way to convert numeric amount into English 开发者_高级运维words

e.g. 12 to twelve 127 to one hundred twenty-seven


That didn't take long. This is an implementation written in Java.

http://snippets.dzone.com/posts/show/3685

Code

public class IntToEnglish {
    static String[] to_19 = { "zero",  "one",   "two",  "three", "four",   "five",   "six",
        "seven", "eight", "nine", "ten",   "eleven", "twelve", "thirteen",
        "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" };
    static String[] tens  = { "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};
    static String[] denom = { "",
        "thousand",     "million",         "billion",       "trillion",       "quadrillion",
        "quintillion",  "sextillion",      "septillion",    "octillion",      "nonillion",
        "decillion",    "undecillion",     "duodecillion",  "tredecillion",   "quattuordecillion",
        "sexdecillion", "septendecillion", "octodecillion", "novemdecillion", "vigintillion" };

    public static void main(String[] argv) throws Exception {
        int tstValue = Integer.parseInt(argv[0]);
        IntToEnglish itoe = new IntToEnglish();
        System.out.println(itoe.english_number(tstValue));
        /* for (int i = 0; i < 2147483647; i++) {
            System.out.println(itoe.english_number(i));
        } */
    }
    // convert a value < 100 to English.
    private String convert_nn(int val) throws Exception {
        if (val < 20)
            return to_19[val];
        for (int v = 0; v < tens.length; v++) {
            String dcap = tens[v];
            int dval = 20 + 10 * v;
            if (dval + 10 > val) {
                if ((val % 10) != 0)
                    return dcap + "-" + to_19[val % 10];
                return dcap;
            }        
        }
        throw new Exception("Should never get here, less than 100 failure");
    }
    // convert a value < 1000 to english, special cased because it is the level that kicks 
    // off the < 100 special case.  The rest are more general.  This also allows you to
    // get strings in the form of "forty-five hundred" if called directly.
    private String convert_nnn(int val) throws Exception {
        String word = "";
        int rem = val / 100;
        int mod = val % 100;
        if (rem > 0) {
            word = to_19[rem] + " hundred";
            if (mod > 0) {
                word = word + " ";
            }
        }
        if (mod > 0) {
            word = word + convert_nn(mod);
        }
        return word;
    }
    public String english_number(int val) throws Exception {
        if (val < 100) {
            return convert_nn(val);
        }
        if (val < 1000) {
            return convert_nnn(val);
        }
        for (int v = 0; v < denom.length; v++) {
            int didx = v - 1;
            int dval = new Double(Math.pow(1000, v)).intValue();
            if (dval > val) {
                int mod = new Double(Math.pow(1000, didx)).intValue();
                int l = val / mod;
                int r = val - (l * mod);
                String ret = convert_nnn(l) + " " + denom[didx];
                if (r > 0) {
                    ret = ret + ", " + english_number(r);
                }
                return ret;
            }
        }
        throw new Exception("Should never get here, bottomed out in english_number");
    }
}


One way to accomplish this would be to use look up tables. It would be somewhat brute force, but easy to setup. For instance, you could have the words for 0-99 in one table, then a table for tens, hundreds, thousands, etc. Some simple math will give you the index of the word you need from each table.


This is some old python code on my hard drive. There might be bugs but it should show the basic idea:

class Translator:
    def transformInt(self, x):
        translateNumbers(0,[str(x)])

    def threeDigitsNumber(self,number):
        snumber=self.twoDigitsNumber(number/100)
        if number/100!=0:
            snumber+=" hundred "
        snumber+self.twoDigitsNumber(number)
        return snumber+self.twoDigitsNumber(number)
    def twoDigitsNumber(self,number):
        snumber=""
        if number%100==10:
            snumber+="ten"
        elif number%100==11:
            snumber+="eleven"
        elif number%100==12:
            snumber+="twelve"
        elif number%100==13:
            snumber+="thirteen"
        elif number%100==14:
            snumber+="fourteen"
        elif number%100==15:
            snumber+="fifteen"
        elif number%100==16:
            snumber+="sixteen"
        elif number%100==17:
            snumber+="seventeen"
        elif number%100==18:
            snumber+="eighteen"
        elif number%100==19:
            snumber+="nineteen"
        else:
            if (number%100)/10==2:
                snumber+="twenty-"
            elif (number%100)/10==3:
                snumber+="thirty-"
            elif (number%100)/10==4:
                snumber+="forty-"
            elif (number%100)/10==5:
                snumber+="fifty-"
            elif (number%100)/10==6:
                snumber+="sixty-"
            elif (number%100)/10==7:
                snumber+="seventy-"
            elif (number%100)/10==8:
                snumber+="eighty-"
            elif (number%100)/10==9:
                snumber+="ninety-"
            if (number%10)==1:
                snumber+="one"
            elif (number%10)==2:
                snumber+="two"
            elif (number%10)==3:
                snumber+="three"
            elif (number%10)==4:
                snumber+="four"
            elif (number%10)==5:
                snumber+="five"
            elif (number%10)==6:
                snumber+="six"
            elif (number%10)==7:
                snumber+="seven"
            elif (number%10)==8:
                snumber+="eight"
            elif (number%10)==9:
                snumber+="nine"
            elif (number%10)==0:
                if snumber!="":
                    if snumber[len(snumber)-1]=="-":
                        snumber=snumber[0:len(snumber)-1]
        return snumber
    def translateNumbers(self,counter,words):
        if counter+1<len(words):
            self.translateNumbers(counter+1,words)
        else:
            if counter==len(words):
                return True
        k=0
        while k<len(words[counter]): 
            if (not (ord(words[counter][k])>47 and ord(words[counter][k])<58)):
                break
            k+=1
        if (k!=len(words[counter]) or k==0):
            return 1
        number=int(words[counter])
        from copy import copy
        if number==0:
            self.translateNumbers(counter+1,copy(words[0:counter]+["zero"]+words[counter+1:len(words)]))
            self.next.append(copy(words[0:counter]+["zero"]+words[counter+1:len(words)]))
            return 1
        if number<10000:
            self.translateNumbers(counter+1,copy(words[0:counter]
                                                      +self.seperatewords(self.threeDigitsNumber(number))
                                                      +words[counter+1:len(words)]))
            self.next.append(copy(words[0:counter]
                                       +self.seperatewords(self.threeDigitsNumber(number))
                                       +words[counter+1:len(words)]))
        if number>999:
            snumber=""
            if number>1000000000:
                snumber+=self.threeDigitsNumber(number/1000000000)+" billion "
                number=number%1000000000
            if number>1000000:
                snumber+=self.threeDigitsNumber(number/1000000)+" million "
                number=number%1000000
            if number>1000:
                snumber+=self.threeDigitsNumber(number/1000)+" thousand "
                number=number%1000
            snumber+=self.threeDigitsNumber(number)
            self.translateNumbers(counter+1,copy(words[0:counter]+self.seperatewords(snumber)
                                                      +words[counter+1:len(words)]))
            self.next.append(copy(words[0:counter]+self.seperatewords(snumber)
                                       +words[counter+1:len(words)]))


This solution doesn't attempt to account for trailing spaces, but it is pretty fast.

typedef const char* cstring;
using std::string;
using std::endl;
std::ostream& GetOnes(std::ostream &output, int onesValue)
{
    cstring ones[] = { "zero", "one", "two", "three", "four", "five", "six", 
           "seven", "eight", "nine" };
    output << ones[onesValue];
    return output;
}

std::ostream& GetSubMagnitude(std::ostream &output, int subMagnitude)
{
    cstring tens[] = { "zeroty", "ten", "twenty", "thirty", "fourty", "fifty", 
         "sixty", "seventy", "eighty", "ninety"};
    if (subMagnitude / 100 != 0)
    {       
        GetOnes(output, subMagnitude / 100) << " hundred ";
        GetSubMagnitude(output, subMagnitude - subMagnitude / 100 * 100);
    }
    else
    {
        if (subMagnitude >= 20)
        {
            output << tens[subMagnitude / 10] << " ";
            GetOnes(output, subMagnitude - subMagnitude / 10 * 10);
        }
        else if (subMagnitude >= 10)
        {
            cstring teens[] = { "ten", "eleven", "twelve", "thirteen",
                        "fourteen", "fifteen", "sixteen", "seventeen", 
                         "eighteen", "nineteen" };
            output << teens[subMagnitude - 10] << " ";
        }
        else 
        {
            GetOnes(output, subMagnitude) << " ";
        }
    }
    return output;
}

std::ostream& GetLongNumber(std::ostream &output, double input)
{
    cstring magnitudes[] = {"", "hundred", "thousand", "million", "billion", 
                           "trillion"};
    double magnitudeTests[] = {1, 100.0, 1000.0, 1000000.0, 1000000000.0, 
                           1000000000000.0 };
    int magTestIndex = 0;
    while (floor(input / magnitudeTests[magTestIndex++]) != 0);
    magTestIndex -= 2;
    if (magTestIndex >= 0) 
    {
        double subMagnitude = input / magnitudeTests[magTestIndex];
        GetSubMagnitude(output, (int)subMagnitude);
        if (magTestIndex) {
            output << magnitudes[magTestIndex] << " ";
            double remainder = input - (floor(input / 
                        magnitudeTests[magTestIndex]) * 
                        magnitudeTests[magTestIndex]);
            if (floor(remainder) > 0) 
            {
                GetLongNumber(output, remainder);
            }
        }
    }
    else
    {
        output << "zero";
    }
    return output;
}


Start by solving 1-99, using a list of numbers for 1-20, and then 30, 40, ..., 90. Then add hundreds to get 1-999. Then use that routine to give the number of each power of 1,000 for as high as you want to go (I think the highest standard nomenclature is for decillion, which is 10^33).

One slight caveat is that it's a little tricky to get the blanks right in all cases if you're trying to start and end without an excess blank. The easy solution is to put a blank after every word, and then strip off the trailing blank when you're all done. If you try to be more precise while building the string, you're likely to end up with missing blanks or excess blanks.


.) make a library of all numbers & positions (e.g. 1 has other notation than 10, another than 100 etc) .) make a list of exceptions (e.g. for 12) and be aware, that in your algorythm, the same exception are for 112, 1012 etc.

if you want even more speed, make a cached set of numbers that you need.


Note some rules:

  • Tens numbers (twenty, thirty, etc.) ending in y are followed by hyphens.
  • Teens are special (except 15-19, but they're still special).
  • Everything else is just some combination of digit place like "three thousand".

You can get the place of a number by using floor division of integers: 532 / 100 -> 5


This is part of Common Lisp!

Here's how GNU CLISP does it, and here's how CMUCL does it (easier to read, IMHO).

Doing a code search for "format million billion" will turn up lots of them.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜