Lingua-EN-Numbers
view release on metacpan or search on metacpan
lib/Lingua/EN/Numbers.pm view on Meta::CPAN
use 5.006;
use strict;
use warnings;
BEGIN { *DEBUG = sub () {0} unless defined &DEBUG } # setup a DEBUG constant
our $VERSION = '2.03';
our @EXPORT = ();
our @EXPORT_OK = qw( num2en num2en_ordinal );
our (%D, %Card2ord, %Mult);
@D{0 .. 20, 30,40,50,60,70,80,90} = qw|
zero
one two three four five six seven eight nine ten
eleven twelve thirteen fourteen fifteen
sixteen seventeen eighteen nineteen
twenty thirty forty fifty sixty seventy eighty ninety
|;
@Card2ord{ qw| one two three five eight nine twelve |}
= qw| first second third fifth eighth ninth twelfth |;
{
my $c = 0;
for ( '', qw<
thousand million billion trillion quadrillion quintillion sextillion
septillion octillion nonillion
> ) {
$Mult{$c} = $_;
$c++;
}
}
#==========================================================================
sub num2en_ordinal {
# Cardinals are [one two three...]
# Ordinals are [first second third...]
return undef unless defined $_[0] and length $_[0];
my($x) = $_[0];
$x = num2en($x);
return $x unless $x;
$x =~ s/(\w+)$//s or return $x . "th";
my $last = $1;
$last =
$Card2ord{$last} || ( $last =~ s/y$/ieth/ && $last ) || ( $last !~ /th$/ ? $last . "th" : $last );
return "$x$last";
}
#==========================================================================
sub num2en {
my $x = $_[0];
return undef unless defined $x and length $x;
return 'not-a-number' if $x eq 'NaN';
return 'positive infinity' if $x =~ m/^\+inf(?:inity)?$/si;
return 'negative infinity' if $x =~ m/^\-inf(?:inity)?$/si;
return 'infinity' if $x =~ m/^inf(?:inity)?$/si;
return $D{$x} if exists $D{$x}; # the most common cases
# Make sure it's not in scientific notation:
{ my $e = _e2en($x); return $e if defined $e; }
my $orig = $x;
$x =~ s/,//g; # nix any commas
my $sign;
$sign = $1 if $x =~ s/^([-+])//s;
my($int, $fract);
if( $x =~ m<^[0-9]+$> ) { $int = $x }
elsif( $x =~ m<^([0-9]+)\.([0-9]+)$> ) { $int = $1; $fract = $2 }
elsif( $x =~ m<^\.([0-9]+)$> ) { $fract = $1 }
else {
DEBUG and print "Not a number: \"orig\"\n";
return undef;
}
DEBUG and printf " Working on Sign[%s] Int2en[%s] Fract[%s] < \"%s\"\n",
map defined($_) ? $_ : "nil", $sign, $int, $fract, $orig;
return join ' ', grep defined($_) && length($_),
_sign2en($sign),
_int2en($int),
_fract2en($fract),
;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub _sign2en {
return undef unless defined $_[0] and length $_[0];
return 'negative' if $_[0] eq '-';
return 'positive' if $_[0] eq '+';
return "WHAT_IS_$_[0]";
}
sub _fract2en { # "1234" => "point one two three four"
return undef unless defined $_[0] and length $_[0];
my $x = $_[0];
return join ' ', 'point', map $D{$_}, split '', $x;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# The real work:
sub _int2en {
return undef unless defined $_[0] and length $_[0]
and $_[0] =~ m/^[0-9]+$/s;
my($x) = $_[0];
return $D{$x} if defined $D{$x}; # most common/irreg cases
lib/Lingua/EN/Numbers.pm view on Meta::CPAN
m<
^(
[-+]? # leading sign
(?:
[0-9,]+ | [0-9,]*\.[0-9]+ # number
)
)
[eE]
([-+]?[0-9]+) # mantissa, has to be an integer
$
>x
) {
($m, $e) = ($1, $2);
DEBUG and print " Scientific notation: [$x] => $m E $e\n";
$e += 0;
return num2en($m) . ' times ten to the ' . num2en_ordinal($e);
} else {
DEBUG and print " Okay, $x isn't in exponential notation\n";
return undef;
}
}
#==========================================================================
1;
__END__
=head1 NAME
Lingua::EN::Numbers - turn "407" into "four hundred and seven", etc.
=head1 SYNOPSIS
use Lingua::EN::Numbers qw(num2en num2en_ordinal);
my $x = 234;
my $y = 54;
print "You have ", num2en($x), " things to do today!\n";
print "You will stop caring after the ", num2en_ordinal($y), ".\n";
prints:
You have two hundred and thirty-four things to do today!
You will stop caring after the fifty-fourth.
=head1 DESCRIPTION
This module provides a function C<num2en>,
which converts a number (such as 123) into English text
("one hundred and twenty-three").
It also provides a function C<num2en_ordinal>,
which converts a number into the ordinal form in words,
so 54 becomes "fifty-fourth".
If you pass either function something that doesn't look like a number,
they will return C<undef>.
This module can handle integers like "12" or "-3" and real numbers like "53.19".
This module also understands exponential notation -- it turns "4E9" into
"four times ten to the ninth"). And it even turns "INF", "-INF", "NaN"
into "infinity", "negative infinity", and "not a number", respectively.
Any commas in the input numbers are ignored.
=head1 LEGACY INTERFACE
The first version of this module, 0.01 released in May 1995,
had an OO interface. This was finally dropped in the 1.08 release.
=head1 SEE ALSO
L<http://neilb.org/reviews/spell-numbers.html> - a review of CPAN modules for converting numbers into English words.
The following modules will convert a number into words:
=over 4
=item
L<Lingua::EN::Inflect> provides a lot more besides, including conversion
of singular to plural, selecting whether to use 'an' or 'a' before a word,
and plenty more
=item
L<Lingua::EN::Nums2Words> provides similar functionality,
but can't handle exponential notation, and 3.14 produces
"three and fourteen hundredths" instead of "three point one four".
=item
L<Math::BigInt::Named> doesn't work.
=item
L<Number::Spell> doesn't handle negative numbers, exponential notation,
or non-integer real numbers. The generated text doesn't contain any commas
or the word 'and', so the results for long numbers don't scan.
=back
There are other modules which provide related, but not identical,
functionality:
=over 4
=item
L<Lingua::EN::Numbers::Ordinate> provides a function that will convert
a cardinal number (such as "3") to an ordinal ("3rd").
=item
L<Lingua::EN::Numbers::Years> provides a function that will convert
a year in numerals (eg "1984") into words ("nineteen eighty-four").
=item
L<Lingua::EN::Fractions> provides a function that will convert
a numeric fraction (eg "3/4") into words ("three quarters").
( run in 2.153 seconds using v1.01-cache-2.11-cpan-524268b4103 )