File Coverage

blib/lib/File/Rsync/Mirror/Recentfile/FakeBigFloat.pm
Criterion Covered Total %
statement 95 102 93.1
branch 39 46 84.7
condition 12 17 70.5
subroutine 15 15 100.0
pod n/a
total 161 180 89.4


line stmt bran cond sub pod time code
1             package File::Rsync::Mirror::Recentfile::FakeBigFloat;
2              
3             # use warnings;
4 8     8   48 use strict;
  8         19  
  8         276  
5 8     8   8331 use Data::Float qw(nextup);
  8         90114  
  8         1522  
6              
7             # _bigfloat
8             sub _bigfloatcmp ($$);
9             sub _bigfloatge ($$);
10             sub _bigfloatgt ($$);
11             sub _bigfloatle ($$);
12             sub _bigfloatlt ($$);
13             sub _bigfloatmax ($$);
14             sub _bigfloatmin ($$);
15             sub _increase_a_bit ($;$);
16             sub _increase_a_bit_tail ($$);
17             sub _my_sprintf_float ($);
18              
19             =encoding utf-8
20              
21             =head1 NAME
22              
23             File::Rsync::Mirror::Recentfile::FakeBigFloat - pseudo bigfloat support
24              
25             =cut
26              
27 8     8   6695 use version; our $VERSION = qv('0.0.8');
  8         18515  
  8         55  
28              
29 8     8   785 use Exporter;
  8         17  
  8         391  
30 8     8   39 use base qw(Exporter);
  8         14  
  8         13380  
31             our %EXPORT_TAGS;
32             our @EXPORT_OK = qw(
33             _bigfloatcmp
34             _bigfloatge
35             _bigfloatgt
36             _bigfloatle
37             _bigfloatlt
38             _bigfloatmax
39             _bigfloatmin
40             _increase_a_bit
41             );
42             $EXPORT_TAGS{all} = \@EXPORT_OK;
43              
44             =head1 SYNOPSIS
45              
46             use File::Rsync::Mirror::Recentfile::FakeBigFloat qw(:all);
47              
48             =head1 ONLY INTERNAL FUNCTIONS
49              
50             These functions are not part of a public interface and can be
51             changed and go away any time without prior notice.
52              
53             =head1 DESCRIPTION
54              
55             We treat strings that look like floating point numbers. If the native
56             floating point support is good enough we use it. If it isn't we make
57             sure no two unequal numbers are treated the same and vice versa. Only
58             comparison operators are supported, no other math.
59              
60             =head1 EXPORT
61              
62             All functions are exported in the C<:all> tag.
63              
64             =head2 _bigfloatcmp ( $l, $r )
65              
66             Cmp function for floating point numbers that have a larger significand
67             than can be handled by native perl floats.
68              
69             =cut
70             sub _bigfloatcmp ($$) {
71             # my($l,$r) = @_;
72 217663 50 33 217663   831818 unless (defined $_[0] and defined $_[1]) {
73 0         0 require Carp;
74 0         0 for ($_[0],$_[1]) {
75 0 0       0 $_ = defined $_ ? $_ : "UNDEF";
76             }
77 0         0 Carp::confess("_bigfloatcmp called with l[$_[0]]r[$_[1]]: but both must be defined");
78             }
79             # unequal is much more frequent than equal but let's get rid of these
80 217663 100       568218 return 0 if $_[0] eq $_[1];
81 193554         223662 my $can_rely_on_native = 0;
82 193554 100 100     555888 if ($_[0] =~ /\./ || $_[1] =~ /\./) {
83             # if one is a float, both must be, otherwise perl gets it wrong (see test)
84 193498         328130 for ($_[0], $_[1]){
85 386996 100       982388 $_ .= ".0" unless /\./;
86             }
87 193498 100       639964 return 1 if $_[0] - $_[1] > 1;
88 165112 100       491745 return -1 if $_[0] - $_[1] < -1;
89             } else {
90 56         71 $can_rely_on_native = 1; # can we?
91             }
92             #### XXX bug in some perls, we cannot trust native comparison on floating point values:
93             #### see Todo file entry on 2009-03-15
94 135970         177496 my $native = $_[0] <=> $_[1];
95 135970 100 66     324447 return $native if $can_rely_on_native && $native != 0;
96 135914         345216 $_[0] =~ s/^/0/ while index($_[0],".") < index($_[1],".");
97 135914         335575 $_[1] =~ s/^/0/ while index($_[1],".") < index($_[0],".");
98 135914         650064 $_[0] cmp $_[1];
99             }
100              
101             =head2 _bigfloatge ( $l, $r )
102              
103             Same for ge
104              
105             =cut
106             sub _bigfloatge ($$) {
107 148376     148376   281435 _bigfloatcmp($_[0],$_[1]) >= 0;
108             }
109              
110             =head2 _bigfloatgt ( $l, $r )
111              
112             Same for gt
113              
114             =cut
115             sub _bigfloatgt ($$) {
116 22032     22032   43708 _bigfloatcmp($_[0],$_[1]) > 0;
117             }
118              
119             =head2 _bigfloatle ( $l, $r )
120              
121             Same for lt
122              
123             =cut
124             sub _bigfloatle ($$) {
125 7715     7715   15257 _bigfloatcmp($_[0],$_[1]) <= 0;
126             }
127              
128             =head2 _bigfloatlt ( $l, $r )
129              
130             Same for lt
131              
132             =cut
133             sub _bigfloatlt ($$) {
134 38577     38577   72188 _bigfloatcmp($_[0],$_[1]) < 0;
135             }
136              
137             =head2 _bigfloatmax ( $l, $r )
138              
139             Same for max (of two arguments)
140              
141             =cut
142             sub _bigfloatmax ($$) {
143 920     920   3143 my($l,$r) = @_;
144 920 100       3159 return _bigfloatcmp($l,$r) >= 0 ? $l : $r;
145             }
146              
147             =head2 _bigfloatmin ( $l, $r )
148              
149             Same for min (of two arguments)
150              
151             =cut
152             sub _bigfloatmin ($$) {
153 43     43   98 my($l,$r) = @_;
154 43 100       172 return _bigfloatcmp($l,$r) <= 0 ? $l : $r;
155             }
156              
157             =head2 $big = _increase_a_bit ( $l, $r )
158              
159             =head2 $big = _increase_a_bit ( $n )
160              
161             The first form calculates a string that is between the two numbers,
162             closer to $l to prevent rounding effects towards $r. The second form
163             calculates the second number itself based on nextup() in
164             L.
165              
166             =cut
167             sub _my_sprintf_float ($) {
168 355     355   51268 my($x) = @_;
169 355         406 my $r;
170 355         1546 require Config;
171 355   50     3428 my $nvsize = $Config::Config{nvsize} || 8;
172 355         981 my $lom = 2*$nvsize; # "length of mantissa": nextup needs more digits
173 355         416 NORMALIZE: while () {
174 714         1285 my $sprintf = "%." . $lom . "f";
175 714         4624 $r = sprintf $sprintf, $x;
176 714 100       2620 if ($r =~ /\.\d+0$/) {
177 355         584 last NORMALIZE;
178             } else {
179 359         552 $lom *= 2;
180             }
181             }
182 355         1779 $r =~ s/(\d)0+$/$1/;
183 355         831 return $r;
184             }
185             sub _increase_a_bit ($;$) {
186 179     179   787 my($l,$r) = @_;
187 179 50       429 unless (defined $l) {
188 0         0 die "Alert: _increase_a_bit called with undefined first argument";
189             }
190 179 100       348 if (defined $r){
191 3 50       14 if ($r eq $l){
    50          
192 0         0 die "Alert: _increase_a_bit called with identical arguments";
193             } elsif ($r > int($l)+1) {
194 0         0 $r = int($l)+1;
195             }
196             } else {
197 176         5503 $r = _my_sprintf_float(Data::Float::nextup($l));
198             }
199 179         266 my $ret;
200 179 50       585 if ($l == $r) {
201             } else {
202             # native try
203 179         691 my $try = _my_sprintf_float(($l+$r)/2);
204 179 100 100     531 if (_bigfloatlt($l,$try) && _bigfloatlt($try,$r) ) {
205 31         54 $ret = $try;
206             }
207             }
208 179 100       561 return $ret if $ret;
209 148         406 return _increase_a_bit_tail($l,$r);
210             }
211             sub _increase_a_bit_tail ($$) {
212 148     148   285 my($l,$r) = @_;
213 148         218 my $ret;
214 148         286 for ($l, $r){
215 296 100       1306 $_ .= ".0" unless /\./;
216             }
217 148         573 $l =~ s/^/0/ while index($l,".") < index($r,".");
218 148         524 $r =~ s/^/0/ while index($r,".") < index($l,".");
219 148         2167 $l .= "0" while length($l) < length($r);
220 148         433 $r .= "0" while length($r) < length($l);
221 148         258 my $diffdigit;
222 148         454 DIG: for (my $i = 0; $i < length($l); $i++) {
223 2637 100       8760 if (substr($l,$i,1) ne substr($r,$i,1)) {
224 148         203 $diffdigit = $i;
225 148         285 last DIG;
226             }
227             }
228 148         316 $ret = substr($l,0,$diffdigit);
229 148         279 my $sl = substr($l,$diffdigit); # significant l
230 148         259 my $sr = substr($r,$diffdigit);
231 148 100       558 if ($ret =~ /\./) {
232 147         708 $sl .= ".0";
233 147         269 $sr .= ".0";
234             }
235 148         286 my $srlength = length $sr;
236 148         281 my $srmantissa = $srlength - index($sr,".");
237             # we want 1+$srlength because if l ends in 99999 and r in 00000,
238             # we need one digit more
239 148         514 my $fformat = sprintf "%%0%d.%df", 1+$srlength, $srmantissa;
240 148         1259 my $appe = sprintf $fformat, ($sl+$sr)/2;
241 148         815 $appe =~ s/(\d)0+$/$1/;
242 148 100       598 if ($ret =~ /\./) {
243 147         330 $appe =~ s/\.//;
244             }
245 148         430 $ret .= $appe;
246 148         232 CHOP: while () {
247 2147         6144 my $try = substr($ret,0,length($ret)-1);
248 2147 100 66     7628 if (_bigfloatlt($l,$try) && _bigfloatlt($try,$r)) {
249 1999         5221 $ret = $try;
250             } else {
251 148         291 last CHOP;
252             }
253             }
254 148         725 return $ret;
255             }
256              
257             =head1 COPYRIGHT & LICENSE
258              
259             Copyright 2008, 2009 Andreas König.
260              
261             This program is free software; you can redistribute it and/or modify it
262             under the same terms as Perl itself.
263              
264             =cut
265              
266             1; # End of File::Rsync::Mirror::Recentfile
267              
268             # Local Variables:
269             # mode: cperl
270             # cperl-indent-level: 4
271             # End: