cardinal(12345) returns "twelve thousand, three hundred forty five". ordinal(12345) returns "twelve thousand, three hundred forty fifth". Supports a wide range, though I don't recall offhand if it reaches the maxints. Should be accurate English for every case.

Very naive with regards to "Perl-fu" elegance. Would be interested to see the optimizations that people suggest. I'm not interested in "I can reduce this to 83 characters," I like being able to read the thing later.

@cardinalscore = qw(zero one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen); @ordinalscore = qw(zeroth first second third fourth fifth sixth seventh eighth ninth tenth eleventh twelfth thirteenth fourteenth fifteenth sixteenth seventeenth eighteenth nineteenth); @cardinaldecade = qw(zero ten twenty thirty forty fifty sixty seventy eighty ninety hundred); @ordinaldecade = qw(zero tenth twentieth thirtieth fortieth fiftieth sixtieth seventieth eightieth ninetieth hundredth); @cardinalgroup = qw(zero thousand million billion trillion); @cardinalgroupvalue = (1, 1000, 1000000, 1000000000, 1000000000000); sub cardinal { local ($number) = @_; my $name = ''; if ($number < 0) { $name .= "negative "; $number = -$number; } elsif ($number == 0) { return $cardinalscore[0]; } my $comma = 0; foreach $group (reverse (1 .. $#cardinalgroup)) { if ($number >= $cardinalgroupvalue[$group]) { my $multigroup = int($number / $cardinalgroupvalue[$group] +); $name .= cardinal($multigroup) . " " . $cardinalgroup[$gro +up]; $number %= $cardinalgroupvalue[$group]; if ($number > 0) { $name .= ", "; } } } if ($number >= 100) { $name .= cardinal(int($number / 100)) . " " . $cardinaldecade[ +10]; $number %= 100; if ($number > 0) { $name .= " "; } } if ($number >= 20) { $name .= $cardinaldecade[int($number / 10)]; $number %= 10; if ($number > 0) { $name .= "-"; } } if ($number > 0) { $name .= $cardinalscore[$number]; } return $name; } sub ordinal { local ($number) = @_; my $name = ''; if ($number < 0) { $name .= "negative "; $number = -$number; } elsif ($number == 0) { return $ordinalscore[0]; } if ($number >= 100) { $name = cardinal($number - ($number % 100)); if (($number % 1000) > 0 && (($number % 1000) - ($number % 100 +)) == 0) { $name .= ","; } $number %= 100; if ($number > 0) { $name .= " "; } } if ($number == 0) { return $name . "th"; } if ($number >= 20) { my $spare = $number % 10; if ($spare == 0) { $name .= $ordinaldecade[int($number / 10)]; } else { $name .= $cardinaldecade[int($number / 10)] . "-"; } $number = $spare; } if ($number > 0) { $name .= $ordinalscore[$number]; } return $name; } sub testcardinalandordinal { foreach $v (-5, 0, 5, 10, 15, 20, 25, 30, 100, 125, 200, 225, 999, 1000, 1001, 1010, 1011, 1035, 2000, 2345, 1000000, 1001001, 123000789, 123456789) { print "'" . cardinal($v) . "' == $v\n"; print "'" . ordinal($v) . "' == $v th\n"; } } # testcardinalandordinal(); 1;

Replies are listed 'Best First'.
Re: cardinal numerals
by t0mas (Priest) on Feb 02, 2001 at 14:21 UTC
    I usually use Lingua::EN::Nums2Words for that type of job.
    use strict; use Lingua::EN::Nums2Words; print &num2word(12345)."\n"; print &num2word_ordinal(12345)."\n";


    /brother t0mas