File Coverage

blib/lib/Math/FixedPoint.pm
Criterion Covered Total %
statement 143 153 93.4
branch 63 64 98.4
condition 10 11 90.9
subroutine 16 17 94.1
pod 1 1 100.0
total 233 246 94.7


line stmt bran cond sub pod time code
1             package Math::FixedPoint;
2             {
3             $Math::FixedPoint::VERSION = '0.20130625.1928';
4             }
5 14     14   417746 use strict;
  14         34  
  14         543  
6 14     14   74 use warnings;
  14         22  
  14         427  
7 14     14   76 use Carp qw(croak);
  14         32  
  14         2738  
8             use overload
9 14         207 '+' => \&_add,
10             '-' => \&_substract,
11             '*' => \&_multiply,
12             '/' => \&_division,
13             '=' => \&_copy,
14             '""' => \&_stringify,
15             'int' => \&_intify,
16             'abs' => \&_absify,
17             '<=>' => \&_num_cmp_tree_way,
18             'cmp' => \&_str_cmp_tree_way,
19 14     14   40405 fallback => 1;
  14         26226  
20              
21             sub new {
22 124     124 1 116317 my ( $class, $num, $radix ) = @_;
23              
24 124         202 my $self;
25              
26 124 100       361 if ( defined $num ) {
27 83         274 my @values = _parse_num( $num, $radix );
28              
29 83         193 $self = \@values;
30             }
31              
32             else {
33 41         98 $self = [ 1, 0, 0 ];
34             }
35              
36 124         444 bless $self, $class;
37             }
38              
39             sub _parse_num {
40 107     107   170 my ( $str, $wanted_radix ) = @_;
41              
42 107 100       1120 if ( int($str) eq $str ) {
    50          
43 14         32 my $value = abs($str);
44 14 100       77 my $sign = $str < 0 ? -1 : 1;
45              
46             return
47 14 100       83 defined $wanted_radix
48             ? ( $sign, $value * 10**$wanted_radix, $wanted_radix )
49             : ( $sign, $value, 0 );
50             }
51              
52             elsif ( $str =~ /^ ([-+]?)(\d*) (?:\.(\d+))? (?:[eE]([-+]?\d+))? $/x ) {
53              
54 93 100 66     699 my $sign = defined $1 && $1 eq '-' ? -1 : 1;
55 93   100     407 my $num = $2 || 0;
56 93   100     359 my $decimal = $3 || '';
57 93   100     516 my $exp = $4 || 0;
58              
59 93         187 my $radix = length($decimal);
60 93         234 $radix -= $exp;
61              
62 93 100       282 my $value =
63             $radix < 0
64             ? $num . $decimal . ( '0' x -$radix )
65             : $num . $decimal;
66              
67 93 100       277 $radix = 0 if $radix < 0;
68              
69             return
70 93 100       582 defined $wanted_radix
71             ? ( $sign, int _coerce( $value, $radix, $wanted_radix ),
72             $wanted_radix )
73             : ( $sign, int $value, $radix );
74             }
75             else {
76 0         0 croak "$str not a valid number";
77             }
78             }
79              
80             sub _coerce {
81 32     32   81 my ( $num, $radix, $wanted_radix ) = @_;
82              
83 32 100       116 return $num if $radix == $wanted_radix;
84              
85 22 100       80 if ( $wanted_radix >= $radix ) {
86 8         59 return $num * 10**( $wanted_radix - $radix );
87             }
88              
89             else {
90 14         29 my $places = $wanted_radix - $radix;
91 14         38 my $reminder = substr( $num, $places );
92 14         30 my $new_num = substr( $num, 0, $places );
93              
94 14 100       98 $new_num++ if $reminder > 5 * 10**( -1 * $places - 1 );
95              
96 14         55 return $new_num;
97             }
98             }
99              
100             sub _copy {
101 0     0   0 my $self = shift;
102 0         0 my $new = Math::FixedPoint->new;
103              
104 0         0 $new->[0] = $self->[0];
105 0         0 $new->[1] = $self->[1];
106 0         0 $new->[2] = $self->[2];
107              
108 0         0 return $new;
109             }
110              
111             sub _add {
112 12     12   262 my ( $self, $num ) = @_;
113              
114 12         28 my ( $sign, $value, $radix ) = @$self;
115              
116 12         17 my $new_sign;
117             my $new_value;
118              
119 12 100       35 if ( ref $num ne 'Math::FixedPoint' ) {
120 10         27 ( $new_sign, $new_value ) = _parse_num( $num, $radix );
121             }
122              
123             else {
124 2         5 $new_sign = $num->[0];
125 2         17 $new_value = _coerce( $num->[1], $num->[2], $radix );
126             }
127              
128 12         36 my $signed_result = $new_sign * $new_value + $sign * $value;
129 12         15 my $unsigned_result = abs($signed_result);
130              
131 12         35 my $new = Math::FixedPoint->new;
132 12 100       36 $new->[0] = $signed_result < 0 ? -1 : 1;
133 12         20 $new->[1] = $unsigned_result;
134 12         18 $new->[2] = $radix;
135              
136 12         39 return $new;
137             }
138              
139             sub _substract {
140 8     8   135 my ( $self, $num, $reverse ) = @_;
141              
142 8         20 my ( $sign, $value, $radix ) = @$self;
143              
144 8         12 my $new_sign;
145             my $new_value;
146              
147 8 100       24 if ( ref $num ne 'Math::FixedPoint' ) {
148 6         17 ( $new_sign, $new_value ) = _parse_num( $num, $radix );
149             }
150              
151             else {
152 2         6 $new_sign = $num->[0];
153 2         11 $new_value = _coerce( $num->[1], $num->[2], $radix );
154             }
155              
156 8 100       26 $new_sign = $reverse ? $new_sign : -1 * $new_sign;
157 8 100       22 $sign = $reverse ? -1 * $sign : $sign;
158              
159 8         17 my $signed_result = $new_sign * $new_value + $sign * $value;
160 8         13 my $unsigned_result = abs($signed_result);
161              
162 8         23 my $new = Math::FixedPoint->new;
163 8 100       27 $new->[0] = $signed_result < 0 ? -1 : 1;
164 8         16 $new->[1] = $unsigned_result;
165 8         17 $new->[2] = $radix;
166              
167 8         26 return $new;
168             }
169              
170             sub _multiply {
171 4     4   106 my ( $self, $num ) = @_;
172              
173 4         15 my ( $sign, $value, $radix ) = @$self;
174              
175 4         6 my $new_sign;
176             my $new_value;
177 0         0 my $new_radix;
178              
179 4 100       16 if ( ref $num ne 'Math::FixedPoint' ) {
180 2         7 ( $new_sign, $new_value, $new_radix ) = _parse_num($num);
181              
182 2         33 $new_value =
183             _coerce( $value * $new_value, $new_radix + $radix, $radix );
184             }
185             else {
186 2         14 $new_sign = $num->[0];
187 2         11 $new_value = _coerce( $value * $num->[1], $radix + $num->[2], $radix );
188             }
189              
190 4         16 my $new = Math::FixedPoint->new;
191              
192 4         10 $new->[0] = $sign * $new_sign;
193 4         9 $new->[1] = $new_value;
194 4         5 $new->[2] = $radix;
195              
196 4         13 return $new;
197             }
198              
199             sub _division {
200 9     9   122 my ( $self, $num, $reverse ) = @_;
201              
202 9         22 my ( $sign, $value, $radix ) = @$self;
203              
204 9         10 my $another_sign;
205             my $another_value;
206 0         0 my $another_radix;
207              
208 9 100       21 if ( ref $num ne 'Math::FixedPoint' ) {
209 5 100       17 $another_sign = $num < 0 ? -1 : 1;
210 5         7 $another_value = abs($num);
211 5         9 $another_radix = 0;
212             }
213              
214             else {
215 4         47 ( $another_sign, $another_value, $another_radix ) = @$num;
216             }
217              
218 9 100       661 croak 'Illegal division by zero' if $another_value == 0;
219              
220 5 100       16 my $result = $reverse ? $another_value / $value : $value / $another_value;
221 5         12 my ( $new_sign, $new_value, $new_radix ) = _parse_num($result);
222              
223 5 100       16 my $extra_radix =
224             $reverse
225             ? $another_radix - $radix
226             : $radix - $another_radix;
227              
228 5         17 $new_value = _coerce( $new_value, $new_radix + $extra_radix, $radix );
229              
230 5         14 my $new = Math::FixedPoint->new;
231              
232 5         11 $new->[0] = $sign * $another_sign;
233 5         9 $new->[1] = $new_value;
234 5         5 $new->[2] = $radix;
235              
236 5         17 return $new;
237             }
238              
239             sub _stringify {
240 29     29   109 my $self = shift;
241              
242 29 100       78 my $sign = $self->[0] < 0 ? '-' : '';
243 29         47 my $value = $self->[1];
244 29         69 my $radix = $self->[2];
245              
246 29 100       158 return "$sign$value" if $radix == 0;
247              
248 19         29 my $length = length($value);
249 19 100       80 return sprintf( "${sign}0.%0${radix}d", $value ) if $length <= $radix;
250              
251 14         29 my $decimal = substr( $value, -$radix );
252 14         22 my $integer = substr( $value, 0, -$radix );
253 14         111 return "$sign$integer.$decimal";
254             }
255              
256             sub _intify {
257 7     7   78 my $self = shift;
258              
259 7         13 my ( $sign, $value, $radix ) = @$self;
260              
261 7         18 my $new = Math::FixedPoint->new;
262 7         11 $new->[2] = 0;
263              
264 7 100       15 if ( $radix == 0 ) {
265 1         2 $new->[0] = $sign;
266 1         2 $new->[1] = $value;
267             }
268              
269             else {
270 6         14 my $new_value = substr $value, 0, -$radix;
271 6   100     17 $new_value ||= 0;
272 6 100       13 $new->[0] = $new_value == 0 ? 1 : $sign;
273 6         13 $new->[1] = $new_value;
274             }
275              
276 7         51 return $new;
277             }
278              
279             sub _absify {
280 5     5   79 my $self = shift;
281              
282 5         12 my ( $sign, $value, $radix ) = @$self;
283              
284 5         14 my $new = Math::FixedPoint->new;
285 5         11 $new->[0] = 1;
286 5         10 $new->[1] = $value;
287 5         9 $new->[2] = $radix;
288              
289 5         161 return $new;
290             }
291              
292             sub _num_cmp_tree_way {
293 5     5   71 my ( $self, $num ) = @_;
294              
295 5         9 my ( $sign1, $value1, $radix1 ) = @$self;
296              
297 5         6 my $sign2;
298             my $value2;
299 0         0 my $radix2;
300              
301 5 100       15 if ( ref $num ne 'Math::FixedPoint' ) {
302 1         3 ( $sign2, $value2, $radix2 ) = _parse_num($num);
303             }
304              
305             else {
306 4         11 ( $sign2, $value2, $radix2 ) = @$num;
307             }
308              
309 5 100       15 $value1 = _coerce( $value1, $radix1, $radix2 ) if $radix2 > $radix1;
310 5 100       17 $value2 = _coerce( $value2, $radix2, $radix1 ) if $radix1 > $radix2;
311              
312 5         45 return $sign1 * $value1 <=> $sign2 * $value2;
313             }
314              
315             sub _str_cmp_tree_way {
316 17     17   2521 my ( $self, $num ) = @_;
317              
318 17         51 $self->_stringify cmp "$num";
319             }
320              
321             1;
322              
323             __END__