Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

How do I convert a number to a readable string?

I would like to implement a function, inWords[],

    inWords[n_]:= ?

which should return a readable English string.

A few expected results:

inWords[123456]
" one hundred twenty three thousand four hundred fifty six"

inWords[1000000001]
"one billion one"

inWords[123456789123456789]

"one hundred twenty three quadrillion four hundred fifty six trillion \
seven hundred eighty nine billion one hundred twenty three million \
four hundred fifty six thousand seven hundred eighty nine"

On the other hand, how do I implement a function to do exactly the reverse of this? That is, to read and parse string and convert the string to a number?

Note The Stack Overflow post Express a number using words in Mathematica tries to answers second part of my question.

However, it uses WolframAlpha which expects one to be connected to the Internet, and there are restrictions on number of calls made so, a non-WolframAlpha answer would be much nicer.

share|improve this question
1  
Is it possible to convert Speak into text? If so that seems like something to try. (SpokenString just returns the number) –  Mike Honeychurch Jan 31 '12 at 22:06
    
I thought about it but if a number grows larger it reads it out using scientific notation !! –  Prashant Bhate Jan 31 '12 at 22:09
    
Yes it does! never tried that before –  Mike Honeychurch Jan 31 '12 at 22:12
    
Maybe you'll like this little thing I made a couple of years ago: 99-bottles-of-beer.net/language-mathematica-1712.html –  Szabolcs Jan 31 '12 at 23:13
    
You might want some localisation in there, UK-GB say 'one hundred and twenty six thousand' –  SLC Feb 1 '12 at 14:35
add comment

7 Answers 7

up vote 11 down vote accepted

Nested WolframAlpha approach, showing the intermediate steps:

numberString[a_, k_: 10] := 
 FixedPointList[
  StringReplace[#, 
    b : (DigitCharacter ..) :> 
     WolframAlpha["spell " <> b, {{"Result", 1}, "Plaintext"}]] &, a, 
  k]

numberString["123456"]

(*
==> {"123456", "123 thousand and 456", "one hundred twenty-three \
thousand and four hundred fifty-six", "one hundred twenty-three \
thousand and four hundred fifty-six"}
*)

numberString["123456789123456789123456789"]

(*
==> {"123456789123456789123456789", "123 septillion, 456 \
sextillion, 789 quintillion, 123 quadrillion, 456 trillion, 789 \
billion, 123 million, 456 thousand and 789", "one hundred \
twenty-three septillion, four hundred fifty-six sextillion, seven \
hundred eighty-nine quintillion, one hundred twenty-three \
quadrillion, four hundred fifty-six trillion, seven hundred \
eighty-nine billion, one hundred twenty-three million, four hundred \
fifty-six thousand and seven hundred eighty-nine", "one hundred \
twenty-three septillion, four hundred fifty-six sextillion, seven \
hundred eighty-nine quintillion, one hundred twenty-three \
quadrillion, four hundred fifty-six trillion, seven hundred \
eighty-nine billion, one hundred twenty-three million, four hundred \
fifty-six thousand and seven hundred eighty-nine"}
*)
share|improve this answer
1  
can SpokenString be made to return the written/textual version of a number if given a number as an argument? It doesn't appear to to that but can you ask someone about that? –  Mike Honeychurch Jan 31 '12 at 22:43
add comment

In Mathematica 8 you can use free form input :

= Spell 15

and you get

"fifteen"

Or just write

= thirty

and you obtain

30

Since for larger numbers this approach yields expressions like {number words number, _ } one could nest this arbitrarily to obtain expressions containing only words. In fact, it is sufficient to nest only two times. For example :

words[x_] := 
  Nest[StringReplace[#, 
                     n : (DigitCharacter ..)  :>
                            WolframAlpha["spell " <> n, 
                                         {{"Result", 1}, "Plaintext"}]
                    ] &,  ToString @ x, 2]
words[8936546956]
(*
 ==>  "eight billion, nine hundred thirty-six million, five 
       hundred forty-six thousand and nine hundred fifty-six"
*)   

On the other hand, writing :

 =   eight billion, nine hundred thirty-six million, five 
     hundred forty-six thousand and nine hundred fifty-six
(*
==>  8936546956
*)
share|improve this answer
1  
This doesn't work for larger numbers. Try for 123456 and it returns "123 thousand and 456" –  rm -rf Jan 31 '12 at 22:21
1  
This works only for small numbers. = spell 123456 yields "123 thousand and 456" –  Sjoerd C. de Vries Jan 31 '12 at 22:21
    
@R.M haha ha ha –  Sjoerd C. de Vries Jan 31 '12 at 22:22
3  
Apply recursively... –  Brett Champion Jan 31 '12 at 22:25
1  
@brett it gets complicated pretty fast. You have to devise a stopping criterion etc. –  Sjoerd C. de Vries Jan 31 '12 at 22:27
show 1 more comment

Messy but a working method

inWords[n_] := Module[
   {r,
    numNames = {"", " one", " two", " three", " four", " five", 
      " six", " seven", " eight", " nine"},
    teenNames = {" ten", " eleven", " twelve", " thirteen", 
      " fourteen", " fifteen", " sixteen", " seventeen", " eighteen", 
      " nineteen"},
    tensNames = {"", " ten", " twenty", " thirty", " forty", " fifty",
       " sixty", " seventy", " eighty", " ninety"}, 
    decimals = {"", " thousand", " million", " billion", " trillion", 
  " quadrillion", " quintillion", " sextillion", " septillion", 
  " octillion", " nonillion", " decillion", " undecillion", 
  " duodecillion", " tredecillion", " quattuordecillion", 
  " quindecillion", " sexdecillion", " septendecillion", 
  " octodecillion", " Novemdecillion", " Vigintillion", " 66illion", 
  " 69illion"}

    }
   ,
   r = If[# != 0, 
        numNames[[# + 1]] <> " hundred" <> 
         If[#2 != 0 || #3 != 0, " and", ""], ""]
       <>
       Switch[#2
        , 0, numNames[[#3 + 1]]
        , 1, teenNames[[#3 + 1]]
        , _, tensNames[[#2 + 1]] <> numNames[[#3 + 1]]
        ]
      & @@@
     (PadLeft[FromDigits /@ Characters@StringReverse@#, 
         3] & /@ StringCases[StringReverse@IntegerString@n, 
        RegularExpression["\\d{1,3}"]]);

   StringJoin@
    Reverse@
     MapThread[
      If[# != "", StringJoin[##], ""] &, {r, Take[decimals, Length@r]}]
   ];

Output

In[279]:= FromDigits@Table[RandomInteger[9], {70}]
inWords@%

Out[279]= \
7317782180245641104170634561625559007936487060051082174021876603737988

Out[280]= " seven 69illion three hundred and seventeen 66illion seven \
hundred and eighty two Vigintillion one hundred and eighty \
Novemdecillion two hundred and forty five octodecillion six hundred \
and forty one septendecillion one hundred and four sexdecillion one \
hundred and seventy quindecillion six hundred and thirty four \
quattuordecillion five hundred and sixty one tredecillion six hundred \
and twenty five duodecillion five hundred and fifty nine undecillion \
seven decillion nine hundred and thirty six nonillion four hundred \
and eighty seven octillion sixty septillion fifty one sextillion \
eighty two quintillion one hundred and seventy four quadrillion \
twenty one trillion eight hundred and seventy six billion six hundred \
and three million seven hundred and thirty seven thousand nine \
hundred and eighty eight"
share|improve this answer
1  
My only objection, at least in American standard English, the "and" is used to refer to the existence of a decimal fraction, e.g. $1.8$ is "one and eight tenths". But, it is not used in numbers without fractional parts, e.g. $185$ is "one hundred eighty five." Additionally, the "and" is used inconsistently in your code it shows up for $1295$, but not for $1095$. –  rcollyer Feb 1 '12 at 1:52
1  
The 'and' should be used for things like 123 =>one hundred and twenty three. It's used in the wrong places above. Also, it shouldn't use the American method, generally, because the American method uses an incomplete and truncated counting system. 1,000,000,000 is 1 thousand million, not 1 billion, which makes using the American system deliver incorrect output at large values. The shorthand is a convenient, but not accurate, counting system. –  MyStream Feb 1 '12 at 8:05
add comment

I used the following to solve Problem 17 from Project Euler (hint hint). It's working on numbers up to 1000, but the grammar of larger numbers is trivial compared to small numbers and should easily be able to be implemented.

Usage:

numberToWord /@ {14, 271, 944}
{"fourteen", "two hundred and seventy-one", "nine hundred and forty-four"}

Source:

numberToWord[n_]:=Module[{result,digits},
    result=ConstantArray["",3]; (* {thousand, hundred, <100} *)
    If[n>=1000,result[[1]]=numberToWord[Quotient[n,1000]]<>" thousand"];
    (* Mod[Quotient[n,100],10] extracts the 100s-digit *)
    If[n>=100\[And]Mod[Quotient[n,100],10]!=0,
        result[[2]]=numberToWord[Mod[Quotient[n,100],10]]<>" hundred";
        If[Mod[n,100]!=0,result[[2]]=result[[2]]<>" and"];
    ];
    digits=Mod[n,100];
    result[[3]]=Which[
        digits==0,"",
        digits==1,"one",
        digits==2,"two",
        digits==3,"three",
        digits==4,"four",
        digits==5,"five",
        digits==6,"six",
        digits==7,"seven",
        digits==8,"eight",
        digits==9,"nine",
        digits==10,"ten",
        digits==11,"eleven",
        digits==12,"twelve",
        digits==13,"thirteen",
        digits==14,"fourteen",
        digits==15,"fifteen",
        digits==16,"sixteen",
        digits==17,"seventeen",
        digits==18,"eighteen",
        digits==19,"nineteen",
        20<=digits<=29,"twenty",
        30<=digits<=39,"thirty",
        40<=digits<=49,"forty",
        50<=digits<=59,"fifty",
        60<=digits<=69,"sixty",
        70<=digits<=79,"seventy",
        80<=digits<=89,"eighty",
        90<=digits<=99,"ninety"
    ];
    If[Mod[n,100]>20\[And]Mod[n,10]!=0,result[[3]]=result[[3]]<>"-"<>numberToWord[Mod[n,10]]];

    (* Join the words spaced *)
    StringJoin[result//Select[#,#!=""&]&//Riffle[#," "]&]
]
share|improve this answer
add comment

This solution is similar in spirit to Prashant's. Though not particularly elegant, I avoid any calls to W|A and any other form of internet connectivity. Further down the post I also provide a solution to the inverse problem of returning the number when given English words.

numberform[n_]:=With[{id=IntegerDigits@n},
                  Partition[PadLeft[id,3Ceiling[Length[id]/3]],3]]

single={"One","Two","Three","Four","Five","Six","Seven","Eight","Nine"};
teen=  {"Ten","Eleven","Twelve","Thirteen","Fourteen","Fifteen",
        "Sixteen","Seventeen","Eighteen","Nineteen"};
double={"Twenty","Thirty","Forty","Fifty","Sixty","Seventy","Eighty","Ninety"};
big=   {"Thousand","Million","Billion","Trillion","Quadrillion",
        "Quintillion","Sextillion","Septillion","Octillion","Nonillion",
        "Decillion","Undecillion","Duodecillion","Tredecillion",
        "Quattuordecillion","Quindecillion","Sexdecillion",
        "Septendecillion","Octodecillion","Novemdecillion","Vigintillion"};

ones[o_]:=Replace[o,Thread[Join[Range[9],{_}]->Join[single,{""}]]]

tens[t_,o_]:=
     Switch[t,0,ones[o],1,Replace[o,Thread[Range[0,9]->teen]],
       _,Replace[t,Thread[Range[2,9]->double]]<>
          If[o==0,""," "<>ones[o]]]

hundreds[arg:{h_,t_,o_}]:=
     Switch[arg,{0,0,0},"",{0,0,_},ones[o],{0,_,_},tens[t,o],_,ones[h]<>
       " Hundred "<>tens[t,o]]

createBlock[b_,n_]:=If[MatchQ[b,""],"",b<>" "<>
     Switch[n,1,"",x_/;x<=22,big[[n-1]]<>
        " ",_,ToString[3 (n-1)]<>"-illion "]]

inWords[h_]:=If[h==0,"Zero",With[{n=numberform[h]},
      StringJoin@@Table[createBlock[hundreds@n[[i]],Length[n]-i+1]
         ,{i,Length[n]}]]]

Here is the example Prashant gave...

In[55]:=
  inWords[7317782180245641104170634561625559007936487060051082174021876603737988]

Out[55]= "Seven 69-illion Three Hundred Seventeen 66-illion Seven Hundred 
Eighty Two Vigintillion One Hundred Eighty Novemdecillion Two Hundred 
Forty Five Octodecillion Six Hundred Forty One Septendecillion One 
Hundred Four Sexdecillion One Hundred Seventy Quindecillion Six 
Hundred Thirty Four Quattuordecillion Five Hundred Sixty One 
Tredecillion Six Hundred Twenty Five Duodecillion Five Hundred Fifty 
Nine Undecillion Seven Decillion Nine Hundred Thirty Six Nonillion 
Four Hundred Eighty Seven Octillion Sixty Septillion Fifty One 
Sextillion Eighty Two Quintillion One Hundred Seventy Four 
Quadrillion Twenty One Trillion Eight Hundred Seventy Six Billion Six 
Hundred Three Million Seven Hundred Thirty Seven Thousand Nine 
Hundred Eighty Eight"

Now to answer part 2 we need to work do the inverse of inWords.

tonum[args___,last_]/;StringMatchQ[last,Alternatives@@big|"*illion"]:=
    blockNum[last]*hundredNum[args]
tonum[args___,last_]:= hundredNum[args,last]
tonum[]:= 0

blockNum[arg:Alternatives@@big]:= 10^(3 Position[big,arg][[1,1]])
blockNum[bigNum_]:=
   Power[10,Sequence@@ToExpression[StringCases[bigNum,DigitCharacter..]]]

oneNum[o_]:= Replace[o,Thread[Join[single,{_}]->Join[Range[1,9],{0}]]]
teenNum[]:= 0;
teenNum[o_]:= Replace[o,Thread[Join[teen,{_}]->Join[Range[10,19],{tenNum[o]}]]]
teenNum[t_,o_]:= tenNum[t,o]
tenNum[t_]:= Replace[t,Thread[Join[double,{_}]->
                Join[Range[20,90,10],{oneNum[t]}]]]
tenNum[t_,o_]:= tenNum[t]+oneNum[o]
hundredNum[h_,"Hundred",t___]:= 100*oneNum[h]+teenNum[t]
hundredNum[h___]:= teenNum[h]

inNumbers[n_]:= Block[{temp=StringSplit[n],split,spans,parts},
   split=Pick[Range[Length[temp]],
              (MemberQ[big,#]||StringMatchQ[#,"*illion"])&/@temp
         ];
   spans=Span@@@Transpose[{Join[{1},split+1],Join[split,{Length[temp]}]}];
   parts=temp[[#]]&/@spans;
   Total[tonum@@@parts]
]

So inNumbers should be able to take a string version of the number and return the digits...

In[51]:= n = 
  7317782180245641104170634561625559007936487060051082174021876603737988;

In[52]:= inNumbers@inWords[n] == n

Out[52]= True
share|improve this answer
add comment
num = 1234567891234567899;
triples = 
  Reverse /@ Reverse@Partition[Reverse@IntegerDigits[num], 3, 3, 1, 0];


threePowers = {"septillion", "sextillion", "quintillion", 
   "quadrillion", "trillion", "billion", "million", "thousand", ""};
singleRules = {0 -> "", 1 -> "one", 2 -> "two", 3 -> "three", 
   4 -> "four", 5 -> "five", 6 -> "six", 7 -> "seven", 8 -> "eight", 
   9 -> "nine"};
tenRules = {0 -> "ten", 1 -> "eleven", 2 -> "twelve", 3 -> "thirteen",
    4 -> "fourteen", 5 -> "fifteen", 6 -> "sixteen", 7 -> "seventeen",
    8 -> "eighteen", 9 -> "nineteen"};
decadeRules = {1 -> "ten", 2 -> "twenty", 3 -> "thirty", 
   4 -> "fourty", 5 -> "fifty", 6 -> "sixty", 7 -> "seventy", 
   8 -> "eighty", 9 -> "ninety"};

rules =
  {
   {0, 0, 0} :> "",
   {0, 0, s_ /; s > 0} :> ToString[s /. singleRules],
   {0, 1, s_} :> ToString[s /. tenRules],
   {0, t_ /; t > 1, s_} :> 
    ToString[t /. decadeRules] <> " " <> ToString[s /. singleRules],
   {h_, 0, 0} :> ToString[h /. singleRules] <> " hundred",
   {h_, 0, s_ /; s > 0} :> 
    ToString[h /. singleRules] <> " hundred and " <> 
     ToString[s /. singleRules],
   {h_, 1, s_} :> 
    ToString[h /. singleRules] <> " hundred and " <> 
     ToString[s /. tenRules],
   {h_, t_ /; t > 1, s_} :> 
    ToString[h /. singleRules] <> " hundred and " <> 
     ToString[t /. decadeRules] <> " " <> ToString[s /. singleRules]
   };


MapThread[
  ToString[#1 /. rules] <> " " <> ToString[#2] <> " " &, {triples, 
   Take[threePowers, -Length[triples]]}] // StringJoin

(*
==> "one quintillion two hundred and thirty four quadrillion \
five hundred and sixty seven trillion eight hundred and ninety one \
billion two hundred and thirty four million five hundred and sixty \
seven thousand eight hundred and ninety nine  "
*)
share|improve this answer
1  
At least in American standard English, "and" is used to refer to the decimal point in a fractional number, e.g. $1.8$ is "one and eight tenths", and not used any where else, e.g. $185$ is "one hundred eighty five." Besides its use is inconsistent in the same manner as this answer. –  rcollyer Feb 1 '12 at 1:56
1  
@rcollyer I'm mostly using British English as that's what I was taught at school and in British English 'and' is normal (en.wikipedia.org/wiki/English_numerals). –  Sjoerd C. de Vries Feb 1 '12 at 13:18
add comment

how do I implement a function to do exactly the reverse of this? That is, to read and parse string and convert the string to a number?

Remove the factor terms.

"One hundred twenty three quadrillion four hundred fifty six trillion seven hundred eighty nine billion one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine"

becomes

"one twenty three four fifty six seven eighty nine one twenty three four fifty six seven eighty nine"

Replace the remaining numerical quantities to get

"1 23 4 65 7 89 1 23 4 56 7 89"

Concatenate the number string to an integer, final result:

`123456789123456789`

So strip all known factor words (e.g. billion), then parse the remaining string for numerical values

So you would be stripping all known factor words, then parse the strings for numerical value (still as string), concatenate the numbers and convert to an integer.

share|improve this answer
2  
Do you have a working code sample? –  rcollyer Feb 1 '12 at 3:35
1  
Stripping the factor words alone won't do it, consider one million and one for example: Removing the factor words, this would become 11. I think those words should be replaced by $10^n$, and then all that's left is converting numbers up to 999 to integer and multiplying it with those powers of 10. –  David Feb 1 '12 at 9:01
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.