File Coverage

blib/lib/Number/Spice.pm
Criterion Covered Total %
statement 38 40 95.0
branch 7 12 58.3
condition 2 2 100.0
subroutine 11 12 91.6
pod 7 7 100.0
total 65 73 89.0


line stmt bran cond sub pod time code
1             package Number::Spice;
2             # $Id: Spice.pm,v 1.7 2000/09/22 15:32:04 verhaege Exp $
3 1     1   801 use strict;
  1         2  
  1         41  
4 1         97 use vars qw($RE_NUMBER $RE_SPICE_SUFFIX $RE_SPICE_NUMBER @RE_SUFFIX_VAL
5 1     1   4 $VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         2  
6 1     1   15 use Exporter;
  1         2  
  1         52  
7 1     1   6 use base qw(Exporter);
  1         7  
  1         494  
8             $VERSION = '0.011';
9             @EXPORT = ();
10             @EXPORT_OK = qw(
11             $RE_NUMBER $RE_SPICE_SUFFIX $RE_SPICE_NUMBER
12             pattern is_spice_number split_spice_number suffix_value
13             spice_to_number number_to_spice normalize_spice_number
14             );
15             %EXPORT_TAGS = (
16             convert => [qw(spice_to_number number_to_spice normalize_spice_number)],
17             re => [qw($RE_NUMBER $RE_SPICE_SUFFIX $RE_SPICE_NUMBER)],
18             all => \@EXPORT_OK,
19             );
20              
21             BEGIN {
22             # regular expression matching a plain number
23 1     1   6 $RE_NUMBER = qr/
24             (?
25             [+-]? # optional sign
26             (?:(?:\d+(?:\.\d*)?)|(?:\.\d+)) # mantissa
27             (?:e[+-]?\d+)? # optional exponent
28             /ix;
29              
30             # regular expression matching a spice suffix (special care is taken not to match an exponent)
31 1         3 $RE_SPICE_SUFFIX = qr/
32             (?:[a-df-z][a-z]*) # any word not starting with E
33             |(?:e[a-z]+) # OR an E followed by other characters
34             /ix;
35              
36             # regular expression matching a spice number
37 1         88 $RE_SPICE_NUMBER = qr/${RE_NUMBER}${RE_SPICE_SUFFIX}?\b/;
38              
39             # list of known spice suffices (as regular expressions) with their numerical value
40 1         436 @RE_SUFFIX_VAL = (
41             [qr/^t/i, 1e12], # tera
42             [qr/^g/i, 1e9], # giga
43             [qr/^(?:x|(?:meg))/i, 1e6], # mega
44             [qr/^k/i, 1e3], # kilo
45             [qr/^m(?!il)/i, 1e-3], # milli
46             [qr/^u/i, 1e-6], # micro
47             [qr/^n/i, 1e-9], # nano
48             [qr/^p/i, 1e-12], # pico
49             [qr/^f/i, 1e-15], # femto
50             [qr/^a/i, 1e-18], # atto
51             [qr/^mil/i, 2.54e-5], # mil (1/1000 inch)
52             );
53             }
54              
55             sub pattern {
56 0     0 1 0 return $RE_SPICE_NUMBER;
57             }
58              
59             sub is_spice_number {
60 1     1 1 108 return $_[0] =~ /^\s*${RE_SPICE_NUMBER}\s*$/; # delimiting whitespace is allowed
61             }
62              
63             sub split_spice_number {
64 20     20 1 18 my $str = shift;
65 20 50       211 if($str =~ /^\s*($RE_NUMBER)($RE_SPICE_SUFFIX?)\s*$/) { # the suffix is optional
66 20   100     113 return ($1,$2 || '');
67             }
68             else { # not a spice number
69 0 0       0 return wantarray ? () : undef;
70             }
71             }
72              
73             sub suffix_value {
74 20     20 1 21 my $suffix = shift;
75              
76             # try all known suffices
77 20         28 foreach(@RE_SUFFIX_VAL) {
78 150 100       485 return $_->[1] if $suffix =~ $_->[0];
79             }
80              
81             # Not a recognized suffix.
82             # It is standard spice policy to discard the suffix in this case,
83             # which corresponds to a multiplication with 1.0.
84 6         22 return 1;
85             }
86              
87             sub spice_to_number {
88 20     20 1 295 my $spice_number = shift;
89 20 50       40 defined($spice_number) or
90             die "No argument given to spice_to_number()";
91              
92 20 50       31 my ($number,$suffix) = split_spice_number($spice_number) or
93             die "Not a spice number: `$spice_number'";
94              
95 20         35 return $number * suffix_value($suffix);
96             }
97              
98             sub number_to_spice {
99 19     19 1 23 my $number = shift;
100 19         24 my $abs_number = abs($number);
101 19         21 my $suffix = '';
102              
103             # find the appropriate suffix
104 19         105 foreach(
105             [1e12, 't'],
106             [1e9, 'g'],
107             [1e6, 'meg'],
108             [1e3, 'k'],
109             [1e0, ''], # if not introduced, 3.14 would be converted to '3140m' !!
110             [1e-3, 'm'],
111             [1e-6, 'u'],
112             [1e-9, 'n'],
113             [1e-12, 'p'],
114             [1e-15, 'f'],
115             [1e-18, 'a'],
116             ) {
117 125 100       225 if($abs_number >= $_->[0]) {
118 17         20 $number /= $_->[0];
119 17         18 $suffix = $_->[1];
120 17         20 last;
121             }
122             }
123             # in case $abs_number < 1E-18, the suffix remains ''
124              
125             # format the adjusted number and suffix into a string
126 19         152 return sprintf("%g$suffix",$number);
127             }
128              
129             sub normalize_spice_number {
130 7     7 1 15 return number_to_spice(spice_to_number($_[0]));
131             }
132              
133             1;
134              
135             __END__