Converting integers to English words

22

7

The goal of this code golf is to convert integers to English words.

The program prompts for input. If this input isn't an integer, print NaN. If it is an integer, convert it to English words and print these words. Minimum input: 0 (zero). Maximum input: 9000 (nine thousand).
So, 5 returns five (case doesn't matter), and 500 returns five hundred or five-hundred (dashes don't matter).

Some other rules:

A one before hundred or thousand is optional: one hundred is correct, but hundred too (if the input is 100 of course).

The word and in for example one hundred and forty five is optional too.

Whitespace matters. So, for 500, five-hundred or five hundred is correct, but fivehundred is not.

Good luck!

ProgramFOX

Posted 2013-10-06T16:53:36.150

Reputation: 8 017

This answer in SO does similar stuff but isn't code-golf. – ST3 – 2013-10-07T12:13:36.387

There is an ungolfed answer here http://www.rgagnon.com/javadetails/java-0426.html .

– None – 2013-10-06T18:29:11.313

Answers

7

Perl 281 bytes

print+0eq($_=<>)?Zero:"@{[((@0=($z,One,Two,Three,Four,Five,@2=(Six,Seven),
Eight,Nine,Ten,Eleven,Twelve,map$_.teen,Thir,Four,@1=(Fif,@2,Eigh,Nine)))
[$_/1e3],Thousand)x($_>999),($0[($_%=1e3)/100],Hundred)x($_>99),
($_%=100)>19?((Twen,Thir,For,@1)[$_/10-2].ty,$0[$_%10]):$0[$_]]}"||NaN

Newlines added for horizontal sanity. The above may be used interactively, or by piping it a value via stdin.

Works correctly for all integer values on the range [0, 19999], values outside this range exhibit undefined behavior. Non-integer values will be truncated towards zero, and as such, only values which are truly non-numeric will report NaN.

Sample usage:

for $n (14, 42, 762, 2000, 6012, 19791, 1e9, foobar, 17.2, -3) {
  print "$n: ", `echo $n | perl spoken-numbers.pl`, $/;
}

Sample output:

14: Fourteen
42: Forty Two
762: Seven Hundred Sixty Two
2000: Two Thousand 
6012: Six Thousand Twelve
19791: Nineteen Thousand Seven Hundred Ninety One
1000000000: Thousand 
foobar: NaN
17.2: Seventeen
-3: Nine Hundred Ninety Seven

primo

Posted 2013-10-06T16:53:36.150

Reputation: 30 891

"1000000000: Thousand "? And shouldn't 17.2 print "NaN"? – DavidC – 2013-10-11T17:46:24.167

5@DavidCarraher "... values outside this range exhibit undefined behavior. Non-integer values will be truncated towards zero, and as such, only values which are truly non-numeric will report NaN." – primo – 2013-10-12T04:20:00.757

I'm not a Perl expert, so I ask this question: does this program prompt for input? – ProgramFOX – 2013-10-14T17:48:14.100

@ProgramFOX I have updated it to read a value from stdin (if run interactively, it will prompt the user for a value), instead of as a function. – primo – 2013-10-15T06:15:22.323

14

JavaScript (375)

Probably a terrible attempt, but anyway, here goes...

alert(function N(s,z){return O="zero,one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thir,,fif,,,eigh,,,,twen,,for".split(","),(z?O[s]||O[s-10]||O[s-20]:s<13?N(s,1):s<20?N(s,1)+"teen":s<100?N(a=20+(s/10|0),1)+"ty"+(s%10?" "+N(s%10):""):s<1e3?N(s/100|0)+" hundred"+(s%100?" "+N(s%100):""):s<1e5?N(s/1e3|0)+" thousand"+(s%1e3?" "+N(s%1e3):""):0)||NaN}(prompt()))

Pretty-printed (as a function):

function N(s,z) {
  return O = "zero,one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thir,,fif,,,eigh,,,,twen,,for".split(","),
      (z? O[s] || O[s-10] || O[s-20]
       : s < 13?  N(s,1)
       : s < 20?  N(s,1) + "teen"
       : s < 100? N(a=20+(s/10|0),1) + "ty" + (s%10?" "+N(s%10):"")
       : s < 1e3?  N(s/100|0) +  " hundred" + (s%100?" "+N(s%100):"")
       : s < 1e5?  N(s/1e3|0) + " thousand" + (s%1e3?" "+N(s%1e3):"") : 0) || NaN
}

Sample conversion (note that it even outputs NaN when out of bounds, i.e. invalid input):

540: five hundred forty
4711: four thousand seven hundred eleven
7382: seven thousand three hundred eighty two
1992: one thousand nine hundred ninety two
hutenosa: NaN
1000000000: NaN
-3: NaN

FireFly

Posted 2013-10-06T16:53:36.150

Reputation: 7 107

+1 its quite difficult to do better in a language like javascript. (you can remove the space in N(s,z) {return to save 1 char) – Math chiller – 2013-10-13T11:04:06.750

Oh, haha, must've missed that one. I also seem to have missed a bunch of chars in the O string. I'll fix that.. – FireFly – 2013-10-13T13:09:14.067

11

Mathematica 60 57

f = ToString@#~WolframAlpha~{{"NumberName", 1}, "Plaintext"} &

Usage:

f[500]

five hundred

Edit:

InputString[]~WolframAlpha~{{"NumberName", 1}, "Plaintext"}

alephalpha

Posted 2013-10-06T16:53:36.150

Reputation: 23 988

3This doesn't really answer the question. I said that the user has to input a number (using the command line, or a prompt box for example), and then your program should output the words (on the command line, or in a message box for example). Your code is just a function to convert it, and your program doesn't ask for input. – ProgramFOX – 2013-10-10T15:37:07.037

@ProgramFOX it says 'The user inputs something'. That does not mean 'The program prompts for input'. – MrZander – 2013-10-11T17:33:15.003

@MrZander: Well, 'The program prompts for input' was actually what I meant. I update my question, but of course, it would be unfair if I wouldn't upvote alephalpha's answer, so he got my +1 – ProgramFOX – 2013-10-12T13:48:36.270

8

Lisp, 72 56 characters

I realize 1) that this is old, and 2) that it relies entirely on the standard library to function, but the fact that you can get the c-lisp printing system to do this kind of thing has always impressed me. Also, this does in fact take the input from a user, convert it, and print it.

(format t "~:[NaN~;~:*~r~]" (parse-integer (read-line) :junk-allowed t))

It totals 72 characters.

  • :junk-allowed causes parse-integer to return nil on failure instead of raising an error.
  • ~:[if-nil~;if-non-nill] conditional predicated on nil, handles NaN where necessary
  • ~:* backs up the argument interpretation to re-consume the input
  • ~r prints the number as an english word string, as requested, except with full corrected punctuation

Sample:

17823658
seventeen million, eight hundred and twenty-three thousand, six hundred and fifty-eight

192hqfwoelkqhwef9812ho1289hg18hoif3h1o98g3hgq
NaN

Lisp info mainly from Practical Common Lisp.

Edit, golfed properly down to 56 characters

(format t "~:[NaN~;~:*~r~]"(ignore-errors(floor(read))))

This version works rather differently. Instead of reading a line and converting it, it invokes the lisp reader to interpret the input as a lisp s-expression, attempts to use it as a number, and if any errors are produced ignores them producing nil to feed the format string conditional. This may be the first instance I've seen of lisp producing a truly terse program... Fun!

  • (read) Invokes the lisp reader/parser to read one expression from standard input and convert it into an appropriate object
  • (floor) attempts to convert any numeric type into the nearest lower integer, non-numeric types cause it to raise an error
  • (ignore-errors ...) does what it says on the tin, it catches and ignores any errors in the enclosed expression, returning nil to feed the NaN branch of the format string

Tom Scogland

Posted 2013-10-06T16:53:36.150

Reputation: 181

It's certainly no problem that the question old :) I edited your answer to include the language name and the character count in a header. – ProgramFOX – 2015-01-10T09:53:23.550

Thank you for the edits, I haven't gotten the Stack* syntax for these things down yet. Went back in and fixed a mistake I made in the description of the conditional in the format string as well. – Tom Scogland – 2015-01-12T18:07:31.537

3

PHP, 327 310 308 bytes

<?$a=['',one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thir,0,fif,0,0,eigh];echo($n=$argv[1])>999?$a[$n/1000].' thousand ':'',$n%1000>99?$a[$n/100%10].' hundred ':'',$n?($k=$n%100)<20?($a[$k]?:$a[$k%10]).[teen][$k<13]:[2=>twen,thir,'for',fif,six,seven,eigh,nine][$k/10].'ty '.$a[$k%10]:zero;

takes the number as parameter, works for 0<=n<=12999

breakdown

// define names
$a=['',one,two,three,four,five,six,seven,eight,nine,
    ten,eleven,twelve,thir,0,fif,0,0,eigh];
// print ...
echo
    ($n=$argv[1])>999?$a[$n/1000].' thousand ':'',                  // thousands
    $n%1000>99?$a[$n/100%10].' hundred ':'',                        // hundreds
    $n?
        // if remains <20:
        ($k=$n%100)<20?
            ($a[$k]?:$a[$k%10]) // no value at index (0,14,16,17,19)? value from index%10
            .[teen][$k<13]      // append "teen" for $k>12
        // else:
        :[2=>twen,thir,'for',fif,six,seven,eigh,nine][$k/10].'ty '  // tens
        .$a[$k%10]                                                  // ones
    // "zero" for $n==0
    :zero
;

Titus

Posted 2013-10-06T16:53:36.150

Reputation: 13 814

2

SAS, 70 characters

data;window w n;display w;if n=. then put 'NaN';else put n words.;run;

The window and display statements open up the SAS command prompt. Input for n goes on line 1. This takes advantage of the SAS format words. which will print the number as a word or series of words with "and", " ", and "-" as appropriate.

Alex A.

Posted 2013-10-06T16:53:36.150

Reputation: 23 761

2

PHP

777 characters

This is definitely a terrible attempt, but you can't accuse me of taking advantage of any loopholes, plus it's a very lucky number. Thanks to ProgramFOX for the tip.

<?php $i=9212;$b = array('zero','one','two','three','four','five','six','seven','eight','nine');$t='teen';$c = array('ten','eleven','tweleve','thir'.$t,$b[4].$t,'fif'.$t,$b[6].$t,$b[7].$t,$b[8].$t,$b[9].$t);$d = array('','','twenty','thirty','fourty','fifty','sixty','seventy','eighty','ninety');$e='hundred';$f='thousand';$j=str_split($i);if (strlen($i)===1){$a=$b[$i];}elseif (strlen($i)===3){$k=1;$a=$b[$j[0]].' '.$e.' '.x($j,$k);}elseif (strlen($i)===4){$k=2;$a=$b[$j[0]].' '.$f.' '.$b[$j[1]].' '.$e.' '.x($j,$k);}elseif (substr($i, -2, 1)==='1'){$a=$c[$j[1]];}else{$a=$d[$j[0]].' '.$b[$j[1]];}$a = str_replace('zero hundred','',$a);echo $a;function x($j,$k){global $i, $b, $c, $d;if (substr($i, -2, 1)==='1'){return $c[$j[$k+1]];}else{return $d[$j[$k]].' '.$b[$j[$k+1]];}}

Long hand

<?php
// Input
$i=9212;
// 0-9
$b = array('zero','one','two','three','four','five','six','seven','eight','nine');
// 10-19 (Very tricky)
$t='teen';
$c = array('ten','eleven','tweleve','thir'.$t,$b[4].$t,'fif'.$t,$b[6].$t,$b[7].$t,$b[8].$t,$b[9].$t); 
// Left digit of 20-99
$d = array('','','twenty','thirty','fourty','fifty','sixty','seventy','eighty','ninety');
// Hundreds
$e='hundred';
// Thousands
$f='thousand';
// Split input
$j=str_split($i);
// 1 digit inputs
if (strlen($i)===1){$a=$b[$i];}
// 3 digit input
elseif (strlen($i)===3){$k=1;$a=$b[$j[0]].' '.$e.' '.x($j,$k);}
// 4 digit input
elseif (strlen($i)===4){$k=2;$a=$b[$j[0]].' '.$f.' '.$b[$j[1]].' '.$e.' '.x($j,$k);}
// 10-19
elseif (substr($i, -2, 1)==='1'){$a=$c[$j[1]];}
// 20-99
else{$a=$d[$j[0]].' '.$b[$j[1]];}
// Fix for thousand numbers
$a = str_replace('zero hundred','',$a);
// Result
echo $a;
// Abstracted function last 2 digits for 3 and 4 digit numbers
function x($j,$k){
    global $i, $b, $c, $d;
    // 10-19
    if (substr($i, -2, 1)==='1'){return $c[$j[$k+1]];}
    // 20-99
    else{return $d[$j[$k]].' '.$b[$j[$k+1]];}
}

Goose

Posted 2013-10-06T16:53:36.150

Reputation: 219

1I think you can shorten your code by creating arrays like this: array('zero','one','two'). – ProgramFOX – 2015-06-26T13:44:34.747

@ProgramFOX or even ['zero','one','two'] (php 5.4+). And if you don't mind E_NOTICE, [zero,one,two] would work as well. – primo – 2015-06-26T21:50:49.980

I should update it, but 777 is such a lucky number. – Goose – 2015-06-26T22:06:28.127

+1 for your efforts. PHP is tragically underrepresented in code golf. – primo – 2015-06-27T05:55:39.840

1

SmileBASIC, 365 Three Hundred Forty Seven bytes

DIM N$[22]D$="OneTwoThreeFourFiveSixSevenEightNineTenElevenTwelveThirFourFifSixSevenEighNineTwenFor
WHILE LEN(D$)INC I,D$[0]<"_
INC N$[I],SHIFT(D$)WEND
INPUT N
W=N MOD 100C%=N/100MOD 10M%=N/1E3T=W<20X=W/10>>0?(N$[M%]+" Thousand ")*!!M%+(N$[C%]+" Hundred ")*!!C%+(N$[X+10+(X==2)*8+(X==4)*7]+"ty "+N$[N MOD 10])*!T+N$[W*T]+"teen"*(T&&W>12)+"Zero"*!N

There's a trailing space if the last one or two digits are 0.

12Me21

Posted 2013-10-06T16:53:36.150

Reputation: 6 110

1

Python 2.x - 378

Derivative of Fireflys answer, although by changing P to include million or trillions, etc.. it could recursively be used for any range of positive numbers. This also supports values up to 999,999

O=",one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thir,,fif,,,eigh,,,,twen,thir,for,fif,,,eigh,".split(",")
P=",thousand".split(',')
def N(s,p=0):
 h,s=divmod(s,1000);x=N(h,p+1)if h>0 else" "
 if s<20:x+=O[s]or O[s-10]+["","teen"][s>12]
 elif s<100:x+=(O[s/10+20]or O[s/10])+"ty"+N(s%10)
 else:x+=N(s/100)+"hundred"+N(s%100)
 return x+" "+P[p]
print N(input())

Sample test (input is <<<, output is >>>):

<<< 1234
>>> one thousand two hundred thirty four

<<< 999999
>>>  nine hundred ninety nine   thousand nine hundred ninety nine

Although, if someone can explain this odd "buffer underflow" issue I have, that'd be swell...

<<< -1
>>>  nine hundred ninety nine

<<< -2
>>>  nine hundred ninety eight

user8777

Posted 2013-10-06T16:53:36.150

Reputation:

print divmod(-2,1000) #-> (-1, 998) – primo – 2013-10-10T07:38:09.640

Oh of course. I was thinking it might take an absolute value or something. But there is -1*1000 and a "remainder" of 998. – None – 2013-10-10T08:57:29.417

0

Python 2, 333 bytes

def f(n):S=str.split;D=S('z one two three four five six seven eight nine');K=' fif six seven eigh nine';k=n/1000;n,m=n/100%10,n%100;e,d=m/10,m%10;return' '.join([k and f(k),'thousand']*(k>0)+[D[n],'hundred']*(n>0)+([S('ten eleven twelve thir four'+K)[d]+'teen'*(d>2)]if 9<m<20else[S('twen thir for'+K)[e-2]+'ty']*(e>0)+[D[d]]*(d>0)))

Try it online!

This is good for 1 to 999,999, inclusive.

Chas Brown

Posted 2013-10-06T16:53:36.150

Reputation: 8 959

0

Pyth, 239 242 bytes

L:rjdb6"  +"dAm+cd;"nine"," one two three four five six seven eight""  twen thir for fif six seven eigh"|y_.ey+Wk.e?Y?thZjd,?hZ+@HhZ"ty"""@GeZ@+c"ten eleven twelve"d+R"teen"+c"thir four"d>H5eZ?hZ+@GhZ" hundred"""c.[03_b]1"thousand"c_jQT3"zero

Input is an integer in range [0-999,999]. Try it online here. Explanation pending.

Previous version, very similar operation, but doesn't support 0:

L:rjdb6"  +"dJc" one two three four five six seven eight nine"dKc"  twen thir for fif six seven eigh nine"dy_.ey+Wk.e?Y?thZjd,?hZ+@KhZ"ty"""@JeZ@+c"ten eleven twelve"d+R"teen"+c"thir four"d>K5eZ?hZ+@JhZ" hundred"""c.[03_b]1"thousand"c_jQT3

Explanation of previous version:

Implicit: Q=eval(input()), d=" "

Step 1: output formatting helper function
L:rjdb6"  +"d   
L               Define a function, y(b):
   jdb          Join b on spaces
  r   6         Strip whitespace from beginning and end
 :              In the above, replace...
       "  +"    ... strings of more than one space...
            d   ... with a single space

Step 2: Define number lookup lists
Jc"..."dKc"..."d   
  "..."            Lookup string
 c     d           Split the above on spaces
J                  Store in J - this is list of unit names
        Kc"..."d   As above, but storing in K - this is list of tens names, without "ty"

Step 3: Bringing it all together
y_.ey+Wk.e?Y?thZjd,?hZ+@KhZ"ty"""@JeZ@+c"ten eleven twelve"d+R"teen"+c"thir four"d>K5eZ?hZ+@JhZ" hundred"""c.[03_b]1"thousand"c_jQT3   
                                                                                                                                jQT    Get digits of Q
                                                                                                                               _       Reverse
                                                                                                                              c    3   Split into groups of 3
  .e                                                                                                                                   Map the above, element as b, index as k, using:
                                                                                                                _b                       Reverse the digits in the group
                                                                                                            .[03                         Pad the above on the left with 0 to length 3
                                                                                                           c      ]1                     Chop at index 1 - [1,2,3] => [[1],[2,3]]
        .e                                                                                                                               Map the above, element as Z, index as Y, using:
          ?Y                                                                                                                               If second element in the group (i.e. tens and units):
            ?thZ                                                                                                                             If (tens - 1) is non-zero (i.e. 0 or >=2):
                   ?hZ                                                                                                                         If tens is non-zero:
                       @KhZ                                                                                                                      Lookup in tens names
                      +    "ty"                                                                                                                  Append "ty"
                                                                                                                                               Else:
                               ""                                                                                                                Empty string
                  ,                                                                                                                            Create two-element list of the above with...
                                 @JeZ                                                                                                          ... lookup units name
                jd                                                                                                                             Join the above on a space - this covers [0-9] and [20-99]
                                                                                                                                             Else:
                                                                     c"thir four"d                                                             ["thir", "four"]
                                                                    +             >K5                                                          Append last 5 element of tens names ("fif" onwards)
                                                            +R"teen"                                                                           Append "teen" to each string in the above
                                      +c"ten eleven twelve"d                                                                                   Prepend ["ten", "eleven", "twelve"]
                                     @                                               eZ                                                        Take string at index of units column - this covers [10-19]
                                                                                                                                           Else: (i.e. hundreds column)
                                                                                       ?hZ                                                   If hundreds column is non-zero:
                                                                                           @JhZ                                                Lookup units name
                                                                                          +    " hundred"                                      Append " hundred"
                                                                                                         ""                                  Else: empty string
                                                                                                                                         Result of map is two element list of [hundreds name, tens and units name]
      Wk                                                                                                                                 If k is nonzero (i.e. dealing with thousands group)...
     +                                                                                                              "thousand"           ... Append "thousand"
    y                                                                                                                                    Apply output formatting (join on spaces, strip, deduplicate spaces)
                                                                                                                                       Result of map is [units group string, thousands group string]
 _                                                                                                                                     Reverse group ordering to put thousands back in front
y                                                                                                                                      Apply output formatting again, implicit print

Sok

Posted 2013-10-06T16:53:36.150

Reputation: 5 592

0

MOO - 55 83 bytes

x=$code_utils:toint(read(p=player));p:tell(x==E_TYPE?"NaN"|$string_utils:english_number(x))

Note: this code does not print any prompt to the standard output.

As a bonus, this code can handle any number withing the bounds of the moo language (2147483647--2147483648).

pppery

Posted 2013-10-06T16:53:36.150

Reputation: 3 987

0

Wolfram Language 27 40 bytes

Making use of the native function, IntegerName,

 Check[Input[]~IntegerName~"Words","NaN"]

The above prompts for user input. The present implementation returns "NaN" if the user enters anything other than an integer.


Some examples (with pre-set inputs):

 Check[243~IntegerName~"Words","NaN"]

two hundred forty-three


 Check[1234567890~IntegerName~"Words","NaN"]   

one billion, two hundred thirty-four million, five hundred sixty-seven thousand, eight hundred ninety


 Check["abc"~IntegerName~"Words","NaN"]  

NaN

DavidC

Posted 2013-10-06T16:53:36.150

Reputation: 24 524