package ScaleUnits;
$VERSION = '0.01';
use strict;
use warnings;
use Carp;
my @si_prefixes = qw(kilo mega giga tera peta exa zetta yotta);
sub new {
my $class = shift;
my %args = @_;
croak "ScaleUnits::new requires named parameter 'unit'\n"
unless defined $args{unit};
%args = (plural => $args{unit} . 's',
factor => 1000,
round => 0,
brief => '',
separator => ' ',
%args);
my $self = \%args;
bless $self, $class;
return $self;
}
sub scale {
my ($self, $value) = @_;
return scale_units($value,
$self->{unit},
$self->{plural},
$self->{factor},
$self->{round},
$self->{brief},
$self->{separator});
}
sub scale_units {
my ($value, $base_unit, $plural, $factor, $round, $brief, $separator
+) = @_;
my $order = 0;
while ($value >= $factor && $order <= $#si_prefixes) {
$value /= $factor;
$order++;
}
$value = int($value*(10**$round))/(10**$round);
# if $order is 0, the number doesn't need a prefix
# otherwise it needs a prefix. by default ($brief is ''), we use the
# full SI prefix. If $brief contains something, we instead use the s
+hort
# and capitalized form of the SI prefix (e.g. 'mega' becomes 'M').
my $si_prefix = ($order == 0 ?
'' :
$brief eq '' ?
$si_prefixes[$order-1] :
uc substr($si_prefixes[$order-1],0,1));
# if $brief is set and we do have a prefix ($order >0), then use $br
+ief
# as the units. Otherwise we have to decide between singular and plu
+ral.
my $units = ($brief ne '' && $order > 0 ?
$brief :
$value == 1 ?
$base_unit :
$plural);
return "$value$separator$si_prefix$units";
}
1;
__END__
=head1 NAME
ScaleUnits - Perl class that takes numbers and formats them nicely, wi
+th
SI prefixes, user-selectable rounding, and more
=head1 SYNOPSIS
use ScaleUnits;
$converter = ScaleUnits->new(
unit => 'byte',
factor => 1024,
round => 2
);
print $converter->scale(123_456_789);
# '117.73 megabytes'
=head1 DESCRIPTION
You want your program to generate nice readable output like this:
Transferred 12 bytes
Transferred 2.14 gigabytes
instead of this:
Transferred 12 byte(s)
Transferred 2243953 byte(s)
Specifically, C<ScaleUnits> can apply SI prefixes from 'kilo' (10**3,
+or 2**10
if you wish) through 'yotta' (10**24). It can round numbers to the num
+ber of
decimal places that you specify, and apply singular and plural forms a
+s
appropriate. It can also use abbreviated forms such as 'MB' and 'GB'.
You define the desired behavior when you construct a C<ScaleUnits> obj
+ect, and
then each time you call the C<scale> method with a specific number, th
+e
appropriate rules are applied and a nicely-formatted string is returne
+d.
=head1 PUBLIC METHODS
=head2 new(%params)
Create a new C<ScaleUnits> object. Pass parameters by name. A C<unit>
parameter is required; others are optional.
=over 4
=item unit
The name of the units you are scaling. Use the singular form:
'byte', 'minute', and so on. By default, C<ScaleUnits> will construct
a plural form by adding an 's' to this base unit, but you can
override this behavior by passing a C<plural> argument.
=item plural
The plural of the units you are scaling. By default, C<ScaleUnits>
creates the plural form of a unit by appending a single 's'.
=item factor
The factor you want to use between successive levels of the SI prefix
hierarchy. By default, this is 1,000. For scaling bytes and other
base-2 numbers, you may want to make it 1,024:
$s = ScaleUnits->new(unit => 'byte', factor => 1024);
Now 'kilo' will be used for a factor of 1,024, 'mega' for a factor of
1,048,976, and so on.
=item round
Round scaled numbers to this many decimal places (maximum). By default
+,
it's 0, and all results will be in whole numbers.
=item brief
Set C<brief> to the units you want to use when a number is large enoug
+h
to require an SI prefix. This will also cause the single-letter form o
+f
SI prefixes to be used. A typical use would be:
ScaleUnits->new(unit => 'byte', brief => 'B');
This will result in output like this:
35 bytes
1 MB
4 GB
By default, C<brief> is a null string, which means that the full SI pr
+efixes
are prepended onto the unit name (singular or plural as needed).
=item separator
Defines the separator that appears between the scaled number and its
accompanying units. By default, it's a space, so results will read
like '24 kilobytes'. By setting C<separator> to a null string you can
get results like '24KB' (no space). Setting C<separator> to a hyphen c
+ould
be useful for generating adjectival forms ('You uploaded a 24-kilobyte
+ file').
=back
=head2 scale($number)
Scale C<$number> using the settings defined when the object was constr
+ucted,
and return the results as a string.
=head1 SEE ALSO
If you want to do conversions between different units -- e.g., meters
+to
inches -- you'll probably want to use a module like C<Math::Units> or
C<Convert::Units> instead.
If you want to handle singular and plural forms for arbitrary terms, t
+ry
C<Lingua::EN::Inflect>.
=head1 BUGS AND LIMITATIONS
Currently provides prefixes only for positive exponents -- there's no
+provision
for converting a fractional number into milli-units, micro-units, and
+so on.
Doesn't currently provide a provision for using the alternate SI prefi
+xes, such
as 'kibi', that I've read have been proposed for base-2 numbers.
=head1 RELEASE AND REVISION HISTORY
0.01 - 4 October 2002 - first release
=head1 AUTHOR AND COPYRIGHT INFORMATION
(C) Copyright 2002 John Clyman (Z<>module-support@clyman.comZ<>,
http://www.clyman.comZ<>). All Rights Reserved.
This program is free software and may be used and redistributed under
+the
terms of the Artistic License.
=cut
|