File Coverage

blib/lib/Number/FormatEng.pm
Criterion Covered Total %
statement 65 65 100.0
branch 36 36 100.0
condition 15 15 100.0
subroutine 12 12 100.0
pod 5 7 71.4
total 133 135 98.5


line stmt bran cond sub pod time code
1             package Number::FormatEng;
2              
3 4     4   94571 use warnings;
  4         8  
  4         124  
4 4     4   20 use strict;
  4         5  
  4         131  
5 4     4   20 use Carp;
  4         25  
  4         495  
6 4     4   3555 use POSIX;
  4         30014  
  4         29  
7 4     4   17185 use Scalar::Util qw(looks_like_number);
  4         11  
  4         3764  
8              
9             require Exporter;
10             our @ISA = qw(Exporter);
11             our @EXPORT_OK = qw(format_eng format_pref unformat_pref use_e_zero no_e_zero);
12             our %EXPORT_TAGS = (all => \@EXPORT_OK);
13              
14             our $VERSION = '0.01';
15              
16             my %prefix = (
17             '-8' => 'y', '8' => 'Y',
18             '-7' => 'z', '7' => 'Z',
19             '-6' => 'a', '6' => 'E',
20             '-5' => 'f', '5' => 'P',
21             '-4' => 'p', '4' => 'T',
22             '-3' => 'n', '3' => 'G',
23             '-2' => 'u', '2' => 'M',
24             '-1' => 'm', '1' => 'k',
25             '0' => '' ,
26             );
27             my %exponent = reverse %prefix;
28              
29             my $no_e_zero = 1;
30              
31             sub use_e_zero {
32 1     1 1 2 $no_e_zero = 0;
33             }
34              
35             sub no_e_zero {
36 1     1 1 3 $no_e_zero = 1;
37             }
38              
39             sub format_pref {
40 42     42 1 2407 return format_num(1, @_);
41             }
42              
43             sub format_eng {
44 36     36 1 4005 return format_num(0, @_);
45             }
46              
47             sub format_num {
48 78     78 0 107 my $prefix_mode = shift;
49 78         90 my $num = shift;
50              
51 78 100       219 my $name = ($prefix_mode) ? 'format_pref' : 'format_eng';
52              
53             # Check validity of input
54 78 100       193 unless (defined $num) {
55 4         666 croak("Error: $name requires numeric input. ",
56             'It seems like no input was provided or input was undefined');
57             }
58 74 100       222 unless (looks_like_number($num)) {
59 8         1402 croak("Error: $name requires numeric input. '$num' is not numeric");
60             }
61              
62 66 100       149 if ($num == 0) {
63 8 100 100     33 if ($prefix_mode or $no_e_zero) {
64 6         31 return '0';
65             }
66             else {
67 2         10 return '0e0';
68             }
69             }
70              
71 58 100       123 my $sign = ($num < 0) ? '-' : '';
72 58         71 $num = abs $num;
73              
74 58 100       122 if ($prefix_mode) {
75 33 100 100     137 if ( ($num >= 1e27) or ($num <= 1e-25) ) {
76             # switch to number exponent mode
77 4         5 $prefix_mode = 0;
78             }
79             }
80              
81 58         297 my $e = floor( log($num) / log(1000) );
82 58         143 my $mult = 1000**$e;
83 58         115 $num = adjust($num / $mult);
84 58 100       101 if ($prefix_mode) {
85 29         185 return $sign . $num . $prefix{$e};
86             }
87             else {
88 29 100 100     158 if ($no_e_zero and ($e == 0)) {
89 8         61 return $sign . $num;
90             }
91             else {
92 21         218 return $sign . $num . 'e' . 3*$e;
93             }
94             }
95             }
96              
97             sub adjust {
98 58     58 0 63 my $num = shift;
99 58 100 100     1014 if ($num < 1) {
    100 100        
    100          
100 2         6 return 1;
101             }
102             elsif (($num < 10) and ($num > 9.999_999_999)) {
103 2         5 return 10;
104             }
105             elsif (($num < 100) and ($num > 99.999_999_99)) {
106 3         7 return 100;
107             }
108             else {
109 51         92 return $num;
110             }
111             }
112              
113             sub unformat_pref {
114 45     45 1 4353 my ($num) = @_;
115              
116             # Check validity of input
117 45 100       101 unless (defined $num) {
118 2         312 croak('Error: unformat_pref requires input. ',
119             'It seems like no input was provided or input was undefined');
120             }
121              
122             # Trim leading and trailing whitespace
123 43         90 $num =~ s/^\s+//;
124 43         60 $num =~ s/\s+$//;
125              
126 43 100       90 unless (length $num) {
127 2         230 croak('Error: unformat_pref requires input. ',
128             'It seems like no input was provided');
129             }
130              
131 41         76 my $prefix = substr $num, -1;
132 41 100       85 if (exists $exponent{$prefix}) {
133 26         33 chop $num;
134 26 100       79 unless (looks_like_number($num)) {
135 2         212 croak("Error: unformat_pref input '$num' is not numeric before prefix '$prefix'");
136             }
137 24         109 $num = $num * (1000**$exponent{$prefix});
138             }
139             else {
140 15 100       45 unless (looks_like_number($num)) {
141 4         446 croak("Error: unformat_pref input '$num' is not numeric");
142             }
143             }
144              
145 35         133 return $num;
146             }
147              
148              
149             =head1 NAME
150              
151             Number::FormatEng - Format a number using engineering notation
152              
153             =head1 VERSION
154              
155             This document refers to Number::FormatEng version 0.01.
156              
157             =head1 SYNOPSIS
158              
159             use Number::FormatEng qw(:all);
160             print format_eng(1234); # prints 1.234e3
161             print format_pref(-0.035); # prints -35m
162             unformat_pref('1.23T'); # returns 1.23e+12
163              
164             =head1 DESCRIPTION
165              
166             Format a number for printing using engineering notation.
167             Engineering notation is similar to scientific notation except that
168             the power of ten must be a multiple of three.
169             Alternately, the number can be formatted using an International
170             System of Units (SI) prefix representing a factor of a thousand.
171              
172             =head1 SUBROUTINES
173              
174             =over 4
175              
176             =item format_eng($number)
177              
178             Format a numeric value using engineering notation. This function
179             returns a string whose exponent is a multiple of 3. Here are some examples:
180              
181             format_eng(1234); # returns 1.234e3
182             format_eng(-0.03); # returns -30e-3
183             format_eng(7.8e7); # returns 78e6
184              
185             In most cases, the precision is preserved. However, rounding will occur
186             if the number of digits is too large (system-dependent). Keep this in
187             mind if C<$number> is a numeric expression. For example, the following
188             may return a different number of digits from system to system:
189              
190             format_eng(1/3);
191              
192             =item format_pref($number)
193              
194             Format a numeric value using engineering notation. This function
195             returns a string using one of the following SI prefixes (representing a
196             power of a thousand):
197              
198             m u n p f a z y
199             k M G T P E Z Y
200              
201             Notice that lower-case C is used instead of the Greek letter Mu.
202              
203             If the number is beyond the prefix ranges (y and Y), then C
204             returns the same formatted string as C. In other words, it
205             does not use an SI prefix.
206              
207             Here are some examples:
208              
209             format_pref(1234); # returns 1.234k
210             format_pref(-0.0004); # returns -400u
211             format_pref(1.27e13); # returns 12.7G
212             format_pref(7.5e60); # returns 7.5e60
213              
214             =item unformat_pref($string)
215              
216             Convert a string formatted using C into a numeric value.
217             Here are some examples:
218              
219             unformat_pref('1.23T'); # returns 1.23e+12
220             unformat_pref('-400u'); # returns -4e-4
221             unformat_pref(37.5); # returns 37.5
222              
223             =item use_e_zero() and no_e_zero()
224              
225             By default, if the exponent is zero, C is not displayed by
226             C. To explicitly display C, use the C method.
227             Use the C method to return to the default behavior.
228              
229             format_eng(55); # returns 55
230             Number::FormatEng::use_e_zero();
231             format_eng(55); # now returns 55e0
232             Number::FormatEng::no_e_zero();
233             format_eng(55); # back to 55
234              
235             =back
236              
237             =head1 EXPORT
238              
239             Nothing is exported by default. Functions may be exported individually, or
240             all functions may be exported at once, using the special tag C<:all>.
241              
242             =head1 DIAGNOSTICS
243              
244             Error conditions cause the program to die using C from the
245             C Core module.
246              
247             =head1 BUGS AND LIMITATIONS
248              
249             There are no known bugs in this module.
250              
251             =head1 SEE ALSO
252              
253             Refer to the following website:
254              
255             L
256              
257             =head1 AUTHOR
258              
259             Gene Sullivan (gsullivan@cpan.org)
260              
261             =head1 ACKNOWLEDGEMENTS
262              
263             Influenced by the following PerlMonks: BrowserUk, GrandFather and repellent.
264              
265             =head1 COPYRIGHT AND LICENSE
266              
267             Copyright (c) 2009 Gene Sullivan. All rights reserved.
268              
269             This module is free software; you can redistribute it and/or modify
270             it under the same terms as Perl itself. See L.
271              
272             =cut
273              
274             1;
275