Names to numbers
Translate the spelled-out English name of a number to a number. You can use a preexisting implementation or roll your own, but you should support inputs up to at least one million (or the maximum value of your language's default bounded integer type, if that's less).
Support for inputs other than positive integers (like zero, negative integers, fractions and floating-point numbers) is optional.
- See also
- Number names — the reverse operation.
D
This uses the D module from the Number names task.
<lang d>import std.stdio, std.array, std.string, std.algorithm, std.bigint,
std.range, number_names;
BigInt bigIntFromWords(in string num) in {
assert(!num.empty);
} body {
auto words = num.replace(",", "").replace(" and ", " ") .replace("-", " ").split;
immutable sign = (words[0] == "minus") ? -1 : +1; if (sign == -1) words = words[1 .. $];
BigInt bsmall, total; foreach (const word; words) { if (small.canFind(word)) { bsmall += small.countUntil(word); } else if (tens.canFind(word)) { bsmall += tens.countUntil(word) * 10; } else if (word == "hundred") { bsmall *= 100; } else if (word == "thousand") { total += bsmall * 1000; bsmall = 0; } else if (huge.canFind(word)) { total += bsmall * BigInt(1000) ^^ huge.countUntil(word); bsmall = 0; } else { immutable msg = format("Don't understand %s part of %s", word, num); throw new Exception(msg); } }
return sign * (total + bsmall);
}
void main() {
foreach (immutable n; iota(-10000, 10000, 17)) assert(n == n.BigInt.spellBigInt.bigIntFromWords);
foreach (immutable p; 0 .. 20) { auto n = 13.BigInt ^^ p; assert(n == n.spellBigInt.bigIntFromWords); }
writeln("This shows <==> for a successful round trip, " ~ " <??> otherwise:"); foreach (immutable n; [0, -3, 5, -7, 11, -13, 17, -19, 23, -29]) { const txt = n.BigInt.spellBigInt; auto num = txt.bigIntFromWords; writefln("%+4d <%s> %s", n, (n == num) ? "==" : "??", txt); } writeln;
long n = 201_021_002_001; while (n) { const txt = n.BigInt.spellBigInt; auto num = txt.bigIntFromWords; writefln("%12d <%s> %s", n, (n == num) ? "==" : "??", txt); n /= -10; } const txt = n.BigInt.spellBigInt; auto num = txt.bigIntFromWords; writefln("%12d <%s> %s", n, (n == num) ? "==" : "??", txt);
}</lang>
- Output:
This shows <==> for a successful round trip, <??> otherwise: +0 <==> zero -3 <==> minus three +5 <==> five -7 <==> minus seven +11 <==> eleven -13 <==> minus thirteen +17 <==> seventeen -19 <==> minus nineteen +23 <==> twenty-three -29 <==> minus twenty-nine 201021002001 <==> two hundred and one billion, twenty-one million, two thousand, and one -20102100200 <==> minus twenty billion, one hundred and two million, one hundred thousand, and two hundred 2010210020 <==> two billion, ten million, two hundred and ten thousand, and twenty -201021002 <==> minus two hundred and one million, twenty-one thousand, and two 20102100 <==> twenty million, one hundred and two thousand, and one hundred -2010210 <==> minus two million, ten thousand, two hundred and ten 201021 <==> two hundred and one thousand, and twenty-one -20102 <==> minus twenty thousand, one hundred and two 2010 <==> two thousand, and ten -201 <==> minus two hundred and one 20 <==> twenty -2 <==> minus two 0 <==> zero
Common Lisp
A counterpart to (format t "~R" ...). <lang Lisp>(defpackage number-names
(:use cl))
(in-package number-names)
(defparameter *ones*
'((one . 1) (two . 2) (three . 3) (four . 4) (five . 5) (six . 6) (seven . 7) (eight . 8) (nine . 9)))
(defparameter *teens*
'((ten . 10) (eleven . 11) (twelve . 12) (thirteen . 13) (fourteen . 14) (fifteen . 15) (sixteen . 16) (seventeen . 17) (eighteen . 18) (nineteen . 19)))
(defparameter *tens*
'((twenty . 20) (thirty . 30) (fourty . 40) (fifty . 50) (sixty . 60) (seventy . 70) (eighty . 80) (ninty . 90)))
(defparameter *hundred*
'((hundred . 100)))
(defparameter *illions*
'((quintillion . 1000000000000000000) (quadrillion . 1000000000000000) (trillion . 1000000000000) (billion . 1000000000) (million . 1000000) (thousand . 1000)))
(defparameter *delims* '(#\Space #\Tab #\Newline #\-))
- Turn a single delimited word into an atom.
(defun tokenize-word (word)
(let ((stream (make-string-output-stream))) (loop do (let ((char (pop word))) (cond ((null char) (return)) ((member char *delims*) (return)) (t (write-char char stream))))) (let ((out (get-output-stream-string stream))) (values (intern (string-upcase out) 'number-names) word))))
- Tokenize the input string.
(defun tokenize (word)
(let ((word (coerce word 'list)) (tokens (list))) (loop do (let ((char (pop word))) (cond ((null char) (return)) ((member char *delims*) nil) (t (multiple-value-bind (token rest-word) (tokenize-word (push char word)) (setf word rest-word) (push token tokens)))))) (reverse tokens)))
- Define a state machine to parse a subsection of a number
- that precedes an -illion.
(defmacro defstate (name end-transitions-p &rest transitions)
(let ((token (gensym "TOKEN")) (number (gensym "NUMBER")) (illions (gensym "ILLIONS")) (illion (gensym "ILLION"))) `(defun ,name (,token ,number ,illions) ,(append '(cond) (loop for trans in transitions collect (destructuring-bind (place to-state op) trans `((assoc ,token ,place) (values ',to-state (,op ,number (cdr (assoc ,token ,place))))))) (when end-transitions-p `(((assoc ,token ,illions) (throw 'done (let ((,illion (assoc ,token ,illions))) (values (* ,number (cdr ,illion)) (car ,illion))))) ((null ,token) (throw 'done (values ,number nil))))) `((t (error "Unexpected token ~a" ,token)))))))
(defstate state-a nil
(*ones* state-b +) (*tens* state-d +) (*teens* state-e +))
(defstate state-b t
(*hundred* state-c *))
(defstate state-c t
(*ones* state-e +) (*tens* state-d +) (*teens* state-e +))
(defstate state-d t
(*ones* state-e +))
(defstate state-e t)
(defun consume-illions (illion illions)
(cond ((null illions) nil) ((eq illion (caar illions)) (cdr illions)) (t (consume-illions illion (cdr illions)))))
- Parse a number up to the next -illion.
- Errors on numbers that (format t "~R" ..)
- would not generate, like "one thousand one million".
(defun parse-sub-number (tokens illions)
(let ((number 0) (state 'state-a)) (multiple-value-bind (number illion) (catch 'done (loop do (let ((token (pop tokens))) (multiple-value-bind (next-state next-number) (funcall state token number illions) (setf state next-state) (setf number next-number))))) (values number (if illion (consume-illions illion illions) illions) tokens))))
- Parse the list of tokenized number parts.
(defun parse-number (tokens)
(let ((illions *illions*) (total 0) (negative-p (eq (car tokens) 'negative))) (when negative-p (pop tokens)) (if (eq (car tokens) 'zero) (if (null (cdr tokens)) 0 (error "Unexpected token ~a" (cadr tokens))) (loop do (multiple-value-bind (number new-illions rest-tokens) (parse-sub-number tokens illions) (setf illions new-illions) (incf total number) (setf tokens rest-tokens) (unless tokens (return (* (if negative-p -1 1) total))))))))
(defun parse (word)
(parse-number (tokenize word)))
(defun test ()
(let ((test-numbers '(+0 -3 +5 -7 +11 -13 +17 -19 +23 -29 201021002001 -20102100201 2010210020 -201021002 20102100 -2010210 201021 -20103 2010 -201 20 -2 0))) (princ "number => (format t \"~R\" number) => (parse (format t \"~R\" number))") (terpri) (mapc (lambda (number) (let ((word (format nil "~R" number))) (format t "~a => ~a => ~a~%" number word (parse word)))) test-numbers)) (values))</lang>
Running the test procedure: <lang none>CL-USER> (number-names::test) number => (format t "~R" number) => (parse (format t "~R" number)) 0 => zero => 0 -3 => negative three => -3 5 => five => 5 -7 => negative seven => -7 11 => eleven => 11 -13 => negative thirteen => -13 17 => seventeen => 17 -19 => negative nineteen => -19 23 => twenty-three => 23 -29 => negative twenty-nine => -29 201021002001 => two hundred one billion twenty-one million two thousand one => 201021002001 -20102100201 => negative twenty billion one hundred two million one hundred thousand two hundred one => -20102100201 2010210020 => two billion ten million two hundred ten thousand twenty => 2010210020 -201021002 => negative two hundred one million twenty-one thousand two => -201021002 20102100 => twenty million one hundred two thousand one hundred => 20102100 -2010210 => negative two million ten thousand two hundred ten => -2010210 201021 => two hundred one thousand twenty-one => 201021 -20103 => negative twenty thousand one hundred three => -20103 2010 => two thousand ten => 2010 -201 => negative two hundred one => -201 20 => twenty => 20 -2 => negative two => -2 0 => zero => 0
- No value</lang>
J
Define the verb usinv to convert number names to numbers. File number_names.ijs contains the code of number names project. <lang J> NB. standard profile defines tolower and delete extra blanks. load'number_names.ijs'
cut =: #@:[ }.&.> [ (E. <;.1 ]) ,
usinv =: 3 : 0
U =. 'ones' ; }. ENU A0 =. ;@:(' and'&cut)^:([: +./ ' and '&E.) tolower deb y NB. standardize to us form. A =. ,&' ones'^:(U -.@e.~ [: {: ;:) A0 B =. ', ' cut A NB. box the comma separated phrases. C =. ' ' cut L:0 B NB. box words within phrases. M =. ENU (1000x ^ #@:[ | (i. {:&>)) C NB. powers of 1000 assert *./ 2 >/\ M NB. the phrases properly ordered. D=. (<'hundred')&cut&> C M +/ .*+/"1 ,"2 (([: (* 100 ^ 2 ~: #) (#EN100)|EN100&i.)&>)D
) </lang>
(-: [&.(us :.usinv))0 1 (-: [&.(us :.usinv))2340202340220204 1 (-: [&.:(us :.usinv))2340202340220204x 1
Perl
The following code reads a file line-by-line. It echos comment lines starting with a hashmark and blank lines. Remaining lines are output followed by an arrow "=>" and any non-negative integer number names translated into numbers, e.g., a line with "ninety-nine" is output like this: "ninety-nine => 99". <lang perl>use strict; use List::Util qw(sum);
our %nums = (
zero => 0, one => 1, two => 2, three => 3, four => 4, five => 5, six => 6, seven => 7, eight => 8, nine => 9, ten => 10, eleven => 11, twelve => 12, thirteen => 13, fourteen => 14, fifteen => 15, sixteen => 16, seventeen => 17, eighteen => 18, nineteen => 19, twenty => 20, thirty => 30, forty => 40, fifty => 50, sixty => 60, seventy => 70, eighty => 80, ninety => 90, hundred => 100, thousand => 1_000, million => 1_000_000, billion => 1_000_000_000, trillion => 1_000_000_000_000, # My ActiveState Win32 Perl uses e-notation after 999_999_999_999_999 quadrillion => 1e+015, quintillion => 1e+018);
- Groupings for thousands, millions, ..., quintillions
our $groups = qr/\d{4}|\d{7}|\d{10}|\d{13}|1e\+015|1e\+018/;
- Numeral or e-notation
our $num = qr/\d+|\d+e\+\d+/;
while (<>) {
# skip blank lines if(/^\s*$/) { print; next; } # echo comment lines if( /^\s*#.*$/ ) { print; next; }
chomp; my $orig = $_; s/-/ /g; # convert hyphens to spaces s/\s\s+/ /g; # remove duplicate whitespace, convert ws to space s/ $//g; # remove trailing blank s/^ //g; # remove leading blank $_ = lc($_); # convert to lower case # tokenize sentence boundaries s/([\.\?\!]) / $1\n/g; s/([\.\?\!])$/ $1\n/g; # tokenize other punctuation and symbols s/\$(.)/\$ $1/g; # prefix s/(.)([\;\:\%',])/$1 $2/g; # suffix
foreach my $key (keys %nums) { s/\b$key\b/$nums{$key}/eg; }
s/(\d) , (\d)/$1 $2/g; s/(\d) and (\d)/$1 $2/g;
s/\b(\d) 100 (\d\d) (\d) (${groups})\b/($1 * 100 + $2 + $3) * $4/eg;
s/\b(\d) 100 (\d\d) (${groups})\b/($1 * 100 + $2) * $3/eg; s/\b(\d) 100 (\d) (${groups})\b/($1 * 100 + $2) * $3/eg; s/\b(\d) 100 (${groups})\b/$1 * $2 * 100/eg;
s/\b100 (\d\d) (\d) (${groups})\b/(100 + $1 + $2) * $3/eg; s/\b100 (\d\d) (${groups})\b/(100 + $1) * $2/eg; s/\b100 (\d) (${groups})\b/(100 + $1) * $2/eg; s/\b100 (${groups})\b/$1 * 100/eg;
s/\b(\d\d) (\d) (${groups})\b/($1 + $2) * $3/eg; s/\b(\d{1,2}) (${groups})\b/$1 * $2/eg;
s/\b(\d\d) (\d) 100\b/($1 + $2) * 100/eg; s/\b(\d{1,2}) 100\b/$1 * 100/eg;
# Date anomolies: nineteen eighty-four and twenty thirteen
s/\b(\d{2}) (\d{2})\b/$1 * 100 + $2/eg;
s/((?:${num} )*${num})/sum(split(" ",$1))/eg;
print $orig, " => ", $_, "\n"; }</lang> Here is a sample input file:
# For numbers between 21 and 99 inclusive, we're supposed to use a hyphen, # but the bank still cashes our check without the hyphen: Seventy-two dollars Seventy two dollars # For numbers bigger than 100, we're not supposed to use "and," # except we still use "and" anyway, e.g., One Hundred and One Dalmatians A Hundred and One Dalmatians One Hundred One Dalmatians Hundred and One Dalmatians One Thousand and One Nights Two Thousand and One: A Space Odyssey # Date anomolies Twenty Thirteen Nineteen Eighty-Four # Maximum value an "unsigned long int" can hold on a 32-bit machine = 2^32 - 1 # define ULONG_MAX 4294967295 four billion, two hundred ninety-four million, nine hundred sixty-seven thousand, two hundred ninety five # Max positive integer on 32-bit Perl is 9_007_199_254_740_992 = 2^53 # Use Math::BigInt if you need more. # Note Perl usually stringifies to 15 digits of precision and this has 16 Nine quadrillion, seven trillion, one hundred ninety-nine billion, two hundred fifty-four million, seven hundred forty thousand, nine hundred ninety two Nine Hundred Ninety-Nine One Thousand One Hundred Eleven Eleven Hundred Eleven Eight Thousand Eight Hundred Eighty-Eight Eighty-Eight Hundred Eighty-Eight Seven Million Seven Hundred Seventy-Seven Thousand Seven Hundred Seventy-Seven Ninety-Nine Trillion Nine Hundred Ninety-Nine Billion Nine Hundred Ninety-Nine Million Nine Hundred Ninety-Nine Thousand Nine Hundred Ninety-Nine ninety-nine three hundred three hundred and ten one thousand, five hundred and one twelve thousand, six hundred and nine five hundred and twelve thousand, six hundred and nine forty-three million, one hundred and twelve thousand, six hundred and nine two billion, one hundred zero eight one hundred one hundred twenty three one thousand one ninety nine thousand nine hundred ninety nine one hundred thousand nine billion one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine one hundred eleven billion one hundred eleven
And here is the resulting output file:
# For numbers between 21 and 99 inclusive, we're supposed to use a hyphen, # but the bank still cashes our check without the hyphen: Seventy-two dollars => 72 dollars Seventy two dollars => 72 dollars # For numbers bigger than 100, we're not supposed to use "and," # except we still use "and" anyway, e.g., One Hundred and One Dalmatians => 101 dalmatians A Hundred and One Dalmatians => a 101 dalmatians One Hundred One Dalmatians => 101 dalmatians Hundred and One Dalmatians => 101 dalmatians One Thousand and One Nights => 1001 nights Two Thousand and One: A Space Odyssey => 2001 : a space odyssey # Date anomolies Twenty Thirteen => 2013 Nineteen Eighty-Four => 1984 # Maximum value an "unsigned long int" can hold on a 32-bit machine = 2^32 - 1 # define ULONG_MAX 4294967295 four billion, two hundred ninety-four million, nine hundred sixty-seven thousand, two hundred ninety five => 4294967295 # Max positive integer on 32-bit Perl is 9_007_199_254_740_992 = 2^53 # Use Math::BigInt if you need more. # Note Perl usually stringifies to 15 digits of precision and this has 16 Nine quadrillion, seven trillion, one hundred ninety-nine billion, two hundred fifty-four million, seven hundred forty thousand, nine hundred ninety two => 9.00719925474099e+015 Nine Hundred Ninety-Nine => 999 One Thousand One Hundred Eleven => 1111 Eleven Hundred Eleven => 1111 Eight Thousand Eight Hundred Eighty-Eight => 8888 Eighty-Eight Hundred Eighty-Eight => 8888 Seven Million Seven Hundred Seventy-Seven Thousand Seven Hundred Seventy-Seven => 7777777 Ninety-Nine Trillion Nine Hundred Ninety-Nine Billion Nine Hundred Ninety-Nine Million Nine Hundred Ninety-Nine Thousand Nine Hundred Ninety-Nine => 99999999999999 ninety-nine => 99 three hundred => 300 three hundred and ten => 310 one thousand, five hundred and one => 1501 twelve thousand, six hundred and nine => 12609 five hundred and twelve thousand, six hundred and nine => 512609 forty-three million, one hundred and twelve thousand, six hundred and nine => 43112609 two billion, one hundred => 2000000100 zero => 0 eight => 8 one hundred => 100 one hundred twenty three => 123 one thousand one => 1001 ninety nine thousand nine hundred ninety nine => 99999 one hundred thousand => 100000 nine billion one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine => 9123456789 one hundred eleven billion one hundred eleven => 111000000111
Perl 6
<lang perl6>my $DEBUG = False;
constant @M = ('ones', 'thousand',
((<m b tr quadr quint sext sept oct non>, (map { (, <un duo tre quattuor quin sex septen octo novem>).flat X~ $_ },
<dec vigint trigint quadragint quinquagint sexagint septuagint octogint nonagint>), 'cent').flat X~ 'illion')).flat;
constant %M = @M Z=> (1, 1000, 1000000 ... *);
grammar Number-Names {
token TOP { ^ [.*? <number>]* .* $ }
method returns {
say callframe(1).subname, ' -> ', self.ast if $DEBUG; self;
} method trimspace {
my $pos = self.pos; --$pos while $pos and self.orig.substr($pos-1,1) ~~ /\s/; self.cursor($pos);
}
token gotsome($oldself) {
<?{ self.pos != $oldself.pos }>
}
token number { :i «
:my $*SOFAR = 0; [ | 'zero' { make 0 } | ('minus' \s+ | 'negative' \s+)? <group>+ % [','\s+|'and'\s+|<?after \s>] <quantnoun> { make [+]($<group>[]».ast) * ($0 ?? -1 !! 1) * $<quantnoun>.ast } ] <.gotsome(self)> <.trimspace> <.returns>
}
token group { :i
<?{ $*SOFAR %% 1000 }> <hundreds> \s* <tweens($*SOFAR + $<hundreds>.ast)> \s* <zillions> <.gotsome(self)> \s*[ $ || <?after \W> || <?before \W> || <?before < th st nd rd th > » > ] { my $group = $<zillions>.ast * ($<hundreds>.ast + $<tweens>.ast); return () if $*SOFAR and $group > $*SOFAR; $*SOFAR += $group; make $group; } <.returns>
}
token hundreds { :i
[ | <date> <!!{ !$*SOFAR }> { make $<date>.ast } | <ones> ['-'|\s*] 'hundred' { make $<ones>.ast * 100 } | <?{ not $*SOFAR }> <tweeny>['-'|\s*] 'hundred' { make $<tweeny>.ast * 100 } | { make 0 } ] <.returns>
}
token tweens($c = 0) { :i
:my $*SOFAR = $c; \s* [ | 'ten' { make 10 } | <teens> <!before \s*'hundred'> { make $<teens>.ast } | <ones> \s+ 'and' \s+ <tens> { make $<ones>.ast + $<tens>.ast } | <scores> \s+ 'and' \s+ <ones> { make $<scores>.ast + $<ones>.ast } | <scores> \s+ 'and' \s+ <tweeny> { make $<scores>.ast + $<tweeny>.ast } | <tens>[['-'|\s*['and'\s*]?]<ones>]? { make $<tens>.ast + ($<ones> ?? $<ones>.ast !! 0) } | <ones> { make $<ones>.ast } | <scores> { make $<scores>.ast } | 'and'\s+ <?{ $c }> <tweens> { make $<tweens>.ast } | (\d+[\.\d+]?) <?{ !$c }> { make +$0 } | { make 0 } ] \s* <.returns>
}
token tweeny {
[ | <ones>[\s+|\-] { make $<ones>.ast } | 'ten'[\s+|\-] { make 10 } | <teens>[\s+|\-] { make $<teens>.ast } | <tens>[[\s*|\-]<ones>]?[\s+|\-] { make $<tens>.ast + ($<ones> ?? $<ones>.ast !! 0) } ]
}
token date { :i
<tweeny> [ | 'ten' { make $<tweeny>.ast * 100 + 10 } | <teens> <!before \s*'hundred'> { make $<tweeny>.ast * 100 + $<teens>.ast } | <tens>[['-'|\s*]<ones>]? { make $<tweeny>.ast * 100 + $<tens>.ast + ($<ones> ?? $<ones>.ast !! 0) } | ['ought'|oh?][\s+|\-]<ones> { make $<tweeny>.ast * 100 + $<ones>.ast } ]
}
token scores { :i
[ | ['one'|'a']?['-'|\s*] 'score' { make 20 } | 'two' ['-'|\s*] 'score' { make 40 } | 'three' ['-'|\s*] 'score' { make 60 } | 'four' ['-'|\s*] 'score' { make 80 } ] <.returns>
}
token teens { :i
[ | 'eleven' { make 11 } | 'twelve' { make 12 } | 'twelf' { make 12 } | 'thirteen' { make 13 } | 'fourteen' { make 14 } | 'fifteen' { make 15 } | 'sixteen' { make 16 } | 'seventeen' { make 17 } | 'eighteen' { make 18 } | 'nineteen' { make 19 } ] <.returns>
}
token tens { :i [ | 'twent'[y|ie] { make 20 } | 'thirt'[y|ie] { make 30 } | 'fort'[y|ie] { make 40 } | 'fift'[y|ie] { make 50 } | 'sixt'[y|ie] { make 60 } | 'sevent'[y|ie] { make 70 } | 'eight'[y|ie] { make 80 } | 'ninet'[y|ie] { make 90 } ] <.returns> }
token ones { :i
[ | 'one' { make 1 } | 'fir'<?before st> { make 1 } | 'two' { make 2 } | 'seco'<?before nd> { make 2 } | 'three' { make 3 } | 'thi'<?before rd> { make 3 } | 'four' { make 4 } | 'five' { make 5 } | 'fif'<?before th> { make 5 } | 'six' { make 6 } | 'seven' { make 7 } | 'eight' { make 8 } | 'eigh'<?before th> { make 8 } | 'nine' { make 9 } | 'nin'<?before th> { make 9 } | <?{ !$*SOFAR }> ['a'\s+]? <?before 'hundred' | 'thousand' | <zillions> <?{ $<zillions>.Str ne }> > { make 1 } ] <.returns>
}
token zillions { :i
[ | (\w+) <?{ %M{lc ~$0} }> { make +%M{lc ~$0} } | <quantnoun> { make $<quantnoun>.ast } | { make 1 } ] <.returns>
}
token quantnoun { :i
\s* [ | 'pair's? ' of'? { make 2 } | 'dozen' { make 12 } | 'gross' ' of'? { make 144 } | { make 1 } ] <.returns>
}
}
sub commify($_) {
/\d\d\d\d\d/
?? .flip.subst(/\d\d\d<?before \d>/, -> $/ {$/~','},:g).flip !! $_; }
sub MAIN ($file = 'numnames') {
$DEBUG = True if $file eq '-'; my $fh = $file eq '-' ?? $*IN !! open($file); for $fh.lines -> $line {
say $line; my $parse = Number-Names.parse($line); if $parse and +$parse<number> { print "\t==> "; my $prev = 0; for $parse<number>[*] -> $n { print substr($line, $prev, $n.from - $prev); print commify(~$n.ast); $prev = $n.to; } say substr($line, $prev); }
}
}</lang>
- Output:
Seventy-two dollars ==> 72 dollars Seventy two dollars ==> 72 dollars One Hundred and One Dalmatians ==> 101 Dalmatians A Hundred and One Dalmatians ==> 101 Dalmatians One Hundred One Dalmatians ==> 101 Dalmatians Hundred and One Dalmatians ==> 101 Dalmatians One Thousand and One Nights ==> 1001 Nights Two Thousand and One: A Space Odyssey ==> 2001: A Space Odyssey math one-oh-one ==> math 101 Charlemagne died in eight-fourteen. ==> Charlemagne died in 814. ten sixty-six ==> 1066 Nineteen Ought Three ==> 1903 Nineteen Eighty-Four ==> 1984 Twenty Thirteen ==> 2013 four billion, two hundred ninety-four million, nine hundred sixty-seven thousand, two hundred ninety five ==> 4,294,967,295 Nine quadrillion, seven trillion, one hundred ninety-nine billion, two hundred fifty-four million, seven hundred forty thousand, nine hundred ninety two ==> 9,007,199,254,740,992 Nine Hundred Ninety-Nine ==> 999 One Thousand One Hundred Eleven ==> 1111 Eleven Hundred Eleven ==> 1111 Eight Thousand Eight Hundred Eighty-Eight ==> 8888 Eighty-Eight Hundred Eighty-Eight ==> 8888 Seven Million Seven Hundred Seventy-Seven Thousand Seven Hundred Seventy-Seven ==> 7,777,777 Ninety-Nine Trillion Nine Hundred Ninety-Nine Billion Nine Hundred Ninety-Nine Million Nine Hundred Ninety-Nine Thousand Nine Hundred Ninety-Nine ==> 99,999,999,999,999 ninety-nine ==> 99 three hundred ==> 300 three hundred and ten ==> 310 one thousand, five hundred and one ==> 1501 twelve thousand, six hundred and nine ==> 12,609 five hundred and twelve thousand, six hundred and nine ==> 512,609 forty-three million, one hundred and twelve thousand, six hundred and nine ==> 43,112,609 two billion, one hundred ==> 2,000,000,100 zero ==> 0 eight ==> 8 one hundred ==> 100 one hundred twenty three ==> 123 one thousand one ==> 1001 ninety nine thousand nine hundred ninety nine ==> 99,999 one hundred thousand ==> 100,000 nine billion one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine ==> 9,123,456,789 one hundred eleven billion one hundred eleven ==> 111,000,000,111 sing a song of sixpence, a pocket full of rye, four and twenty blackbirds baked in a pie ==> sing a song of sixpence, a pocket full of rye, 24 blackbirds baked in a pie Fourscore and seven years ago, our four fathers... ==> 87 years ago, our 4 fathers... The Ninety and Nine ==> The 99 Seven Brides for Seven Brothers ==> 7 Brides for 7 Brothers Cheaper by the Dozen ==> Cheaper by the 12 five dozen roses ==> 60 roses a dozen dozen roses ==> 144 roses two gross of eggs ==> 288 eggs two hundred pairs of socks ==> 400 socks The First Noël ==> The 1st Noël second place ==> 2nd place Forty-second Street ==> 42nd Street the twenty-third Psalm ==> the 23rd Psalm The Fourth Estate ==> The 4th Estate The Fifth Essence ==> The 5th Essence The sixth sick shiek's sixth sheep's sick. ==> The 6th sick shiek's 6th sheep's sick. in seventh heaven ==> in 7th heaven The Eighth Wonder of the World! ==> The 8th Wonder of the World! Malher's Ninth Symphony ==> Malher's 9th Symphony a tenth of all he had ==> a 10th of all he had the eleventh hour ==> the 11th hour Twelfth Night ==> 12th Night Friday the Thirteenth ==> Friday the 13th Twentieth-Century Fox ==> 20th-Century Fox one novemseptuagintillion, one octoseptuagintillion, one septenseptuagintillion, one sexseptuagintillion, one quinseptuagintillion, one quattuorseptuagintillion, one treseptuagintillion, one duoseptuagintillion, one unseptuagintillion, one septuagintillion, one novemsexagintillion, one octosexagintillion, one septensexagintillion, one sexsexagintillion, one quinsexagintillion, one quattuorsexagintillion, one tresexagintillion, one duosexagintillion, one unsexagintillion, one sexagintillion, one novemquinquagintillion, one octoquinquagintillion, one septenquinquagintillion, one sexquinquagintillion, one quinquinquagintillion, one quattuorquinquagintillion, one trequinquagintillion, one duoquinquagintillion, one unquinquagintillion, one quinquagintillion, one novemquadragintillion, one octoquadragintillion, one septenquadragintillion, one sexquadragintillion, one quinquadragintillion, one quattuorquadragintillion, one trequadragintillion, one duoquadragintillion, one unquadragintillion, one quadragintillion, one novemtrigintillion, one octotrigintillion, one septentrigintillion, one sextrigintillion, one quintrigintillion, one quattuortrigintillion, one tretrigintillion, one duotrigintillion, one untrigintillion, one trigintillion, one novemvigintillion, one octovigintillion, one septenvigintillion, one sexvigintillion, one quinvigintillion, one quattuorvigintillion, one trevigintillion, one duovigintillion, one unvigintillion, one vigintillion, one novemdecillion, one octodecillion, one septendecillion, one sexdecillion, one quindecillion, one quattuordecillion, one tredecillion, one duodecillion, one undecillion, one decillion, one nonillion, one octillion, one septillion, one sextillion, one quintillion, one quadrillion, one trillion, one billion, one million, one thousand, one ==> 1,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001,001
Python
This example assumes that the module from Number_names#Python is stored as spell_integer.py.
The example understands the textual format generated from number-to-names module.
Note: This example and Number_names#Python need to be kept in sync <lang python>from spell_integer import spell_integer, SMALL, TENS, HUGE
def int_from_words(num):
words = num.replace(',',).replace(' and ', ' ').replace('-', ' ').split() if words[0] == 'minus': negmult = -1 words.pop(0) else: negmult = 1 small, total = 0, 0 for word in words: if word in SMALL: small += SMALL.index(word) elif word in TENS: small += TENS.index(word) * 10 elif word == 'hundred': small *= 100 elif word == 'thousand': total += small * 1000 small = 0 elif word in HUGE: total += small * 1000 ** HUGE.index(word) small = 0 else: raise ValueError("Don't understand %r part of %r" % (word, num)) return negmult * (total + small)
if __name__ == '__main__':
# examples for n in range(-10000, 10000, 17): assert n == int_from_words(spell_integer(n))
for n in range(20): assert 13**n == int_from_words(spell_integer(13**n)) print('\n##\n## These tests show <==> for a successful round trip, otherwise <??>\n##\n') for n in (0, -3, 5, -7, 11, -13, 17, -19, 23, -29): txt = spell_integer(n) num = int_from_words(txt) print('%+4i <%s> %s' % (n, '==' if n == num else '??', txt)) print() n = 201021002001 while n: txt = spell_integer(n) num = int_from_words(txt) print('%12i <%s> %s' % (n, '==' if n == num else '??', txt)) n //= -10 txt = spell_integer(n) num = int_from_words(txt) print('%12i <%s> %s' % (n, '==' if n == num else '??', txt)) print()</lang>
- Output:
## ## These tests show <==> for a successful round trip, otherwise <??> ## +0 <==> zero -3 <==> minus three +5 <==> five -7 <==> minus seven +11 <==> eleven -13 <==> minus thirteen +17 <==> seventeen -19 <==> minus nineteen +23 <==> twenty-three -29 <==> minus twenty-nine 201021002001 <==> two hundred and one billion, twenty-one million, two thousand, and one -20102100201 <==> minus twenty billion, one hundred and two million, one hundred thousand, two hundred and one 2010210020 <==> two billion, ten million, two hundred and ten thousand, and twenty -201021002 <==> minus two hundred and one million, twenty-one thousand, and two 20102100 <==> twenty million, one hundred and two thousand, and one hundred -2010210 <==> minus two million, ten thousand, two hundred and ten 201021 <==> two hundred and one thousand, and twenty-one -20103 <==> minus twenty thousand, one hundred and three 2010 <==> two thousand, and ten -201 <==> minus two hundred and one 20 <==> twenty -2 <==> minus two 0 <==> zero
Ruby
This solution uses "Number names" from here
<lang ruby>require 'number_names'
def int_from_words(num)
words = num.downcase.gsub(/(,| and |-)/,' ').split if words[0] =~ /(minus|negative)/ negmult = -1 words.shift else negmult = 1 end small, total = 0, 0 for word in words case word when *SMALL small += SMALL.index(word) when *TENS small += TENS.index(word) * 10 when 'hundred' small *= 100 when 'thousand' total += small * 1000 small = 0 when *BIG total += small * 1000 ** BIG.index(word) small = 0 else raise ArgumentError, "Don't understand %s part of %s" % [word, num] end end negmult * (total + small)
end</lang>
Examples: <lang ruby>for n in (-10000..10000).step(17)
raise unless n == int_from_words(wordify(n))
end
for n in 0...20
raise unless 13**n == int_from_words(wordify(13**n))
end
puts "##\n## These tests show <==> for a successful round trip, otherwise <??>\n##" for n in [0, -3, 5, -7, 11, -13, 17, -19, 23, -29]
txt = wordify(n) num = int_from_words(txt) puts '%+4i <%s> %s' % [n, n==num ? '==' : '??', txt]
end puts
n = 201021002001 loop do
txt = wordify(n) num = int_from_words(txt) puts '%12i <%s> %s' % [n, n==num ? '==' : '??', txt] break if n==0 n /= -10
end</lang>
- Output:
## ## These tests show <==> for a successful round trip, otherwise <??> ## +0 <==> zero -3 <==> negative three +5 <==> five -7 <==> negative seven +11 <==> eleven -13 <==> negative thirteen +17 <==> seventeen -19 <==> negative nineteen +23 <==> twenty-three -29 <==> negative twenty-nine 201021002001 <==> two hundred and one billion, twenty-one million, two thousand, one -20102100201 <==> negative twenty billion, one hundred and two million, one hundred thousand, two hundred and one 2010210020 <==> two billion, ten million, two hundred and ten thousand, twenty -201021002 <==> negative two hundred and one million, twenty-one thousand, two 20102100 <==> twenty million, one hundred and two thousand, one hundred -2010210 <==> negative two million, ten thousand, two hundred and ten 201021 <==> two hundred and one thousand, twenty-one -20103 <==> negative twenty thousand, one hundred and three 2010 <==> two thousand, ten -201 <==> negative two hundred and one 20 <==> twenty -2 <==> negative two 0 <==> zero
Tcl
<lang tcl>package require Tcl 8.6 proc name2num name {
set words [regexp -all -inline {[a-z]+} [string tolower $name]] set tokens {
"zero" 0 "one" 1 "two" 2 "three" 3 "four" 4 "five" 5 "six" 6 "seven" 7 "eight" 8 "nine" 9 "ten" 10 "eleven" 11 "twelve" 12 "thirteen" 13 "fourteen" 14 "fifteen" 15 "sixteen" 16 "seventeen" 17 "eighteen" 18 "nineteen" 19 "twenty" 20 "thirty" 30 "forty" 40 "fifty" 50 "sixty" 60 "seventy" 70 "eighty" 80 "ninety" 90 "hundred" 100 "thousand" 1000 "million" 1000000 "billion" 1000000000 "trillion" 1000000000000 "quadrillion" 1000000000000000 "qintillion" 1000000000000000000
} set values {} set groups {} set previous -inf set sign 1 foreach word $words {
if {[dict exists $tokens $word]} { set value [dict get $tokens $word] if {$value < $previous} { # Check if we have to propagate backwards the "large" terms if {[set mult [lindex $values end]] > 99} { for {set i [llength $groups]} {[incr i -1] >= 0} {} { if {[lindex $groups $i end] >= $mult} { break } lset groups $i end+1 $mult } } lappend groups $values set values {} } elseif {$value < 100 && $previous < 100 && $previous >= 0} { # Special case: dates lappend groups [lappend values 100] set values {} } lappend values $value set previous $value } elseif {$word eq "minus"} { set sign -1 }
} lappend groups $values set groups [lmap prodgroup $groups {tcl::mathop::* {*}$prodgroup}] # Special case: dates if {[llength $groups] == 2} {
if {[lmap g $groups {expr {$g < 100 && $g >= 10}}] eq {1 1}} { lset groups 0 [expr {[lindex $groups 0] * 100}] }
} return [expr {$sign * [tcl::mathop::+ {*}$groups]}]
}</lang> Demonstrating/testing (based on Perl code's samples): <lang tcl>set samples {
"Seventy-two dollars" "Seventy two dollars" "One Hundred and One Dalmatians" "A Hundred and One Dalmatians" "One Hundred One Dalmatians" "Hundred and One Dalmatians" "One Thousand and One Nights" "Two Thousand and One: A Space Odyssey" "Twenty Thirteen" "Nineteen Eighty-Four" "four billion, two hundred ninety-four million, nine hundred sixty-seven thousand, two hundred ninety five" "Nine quadrillion, seven trillion, one hundred ninety-nine billion, two hundred fifty-four million, seven hundred forty thousand, nine hundred ninety two" "Nine Hundred Ninety-Nine" "One Thousand One Hundred Eleven" "Eleven Hundred Eleven" "Eight Thousand Eight Hundred Eighty-Eight" "Eighty-Eight Hundred Eighty-Eight" "Seven Million Seven Hundred Seventy-Seven Thousand Seven Hundred Seventy-Seven" "Ninety-Nine Trillion Nine Hundred Ninety-Nine Billion Nine Hundred Ninety-Nine Million Nine Hundred Ninety-Nine Thousand Nine Hundred Ninety-Nine" "ninety-nine" "three hundred" "three hundred and ten" "one thousand, five hundred and one" "twelve thousand, six hundred and nine" "five hundred and twelve thousand, six hundred and nine" "forty-three million, one hundred and twelve thousand, six hundred and nine" "two billion, one hundred" "zero" "eight" "one hundred" "one hundred twenty three" "one thousand one" "ninety nine thousand nine hundred ninety nine" "one hundred thousand" "nine billion one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine" "one hundred eleven billion one hundred eleven" "minus fifty six"
} foreach s $samples {
puts "$s => [name2num $s]"
}</lang>
- Output:
Seventy-two dollars => 72 Seventy two dollars => 72 One Hundred and One Dalmatians => 101 A Hundred and One Dalmatians => 101 One Hundred One Dalmatians => 101 Hundred and One Dalmatians => 101 One Thousand and One Nights => 1001 Two Thousand and One: A Space Odyssey => 2001 Twenty Thirteen => 2013 Nineteen Eighty-Four => 1984 four billion, two hundred ninety-four million, nine hundred sixty-seven thousand, two hundred ninety five => 4294967295 Nine quadrillion, seven trillion, one hundred ninety-nine billion, two hundred fifty-four million, seven hundred forty thousand, nine hundred ninety two => 9007199254740992 Nine Hundred Ninety-Nine => 999 One Thousand One Hundred Eleven => 1111 Eleven Hundred Eleven => 1111 Eight Thousand Eight Hundred Eighty-Eight => 8888 Eighty-Eight Hundred Eighty-Eight => 8888 Seven Million Seven Hundred Seventy-Seven Thousand Seven Hundred Seventy-Seven => 7777777 Ninety-Nine Trillion Nine Hundred Ninety-Nine Billion Nine Hundred Ninety-Nine Million Nine Hundred Ninety-Nine Thousand Nine Hundred Ninety-Nine => 99999999999999 ninety-nine => 99 three hundred => 300 three hundred and ten => 310 one thousand, five hundred and one => 1501 twelve thousand, six hundred and nine => 12609 five hundred and twelve thousand, six hundred and nine => 512609 forty-three million, one hundred and twelve thousand, six hundred and nine => 43112609 two billion, one hundred => 2000000100 zero => 0 eight => 8 one hundred => 100 one hundred twenty three => 123 one thousand one => 1001 ninety nine thousand nine hundred ninety nine => 99999 one hundred thousand => 100000 nine billion one hundred twenty three million four hundred fifty six thousand seven hundred eighty nine => 9123456789 one hundred eleven billion one hundred eleven => 111000000111 minus fifty six => -56