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   50 use strict;
  8         17  
  8         253  
5 8     8   4781 use Data::Float qw(nextup);
  8         72893  
  8         1162  
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   3401 use version; our $VERSION = qv('0.0.8');
  8         14912  
  8         49  
28              
29 8     8   848 use Exporter;
  8         16  
  8         307  
30 8     8   1897 use base qw(Exporter);
  8         19  
  8         11896  
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 225568 50 33 225568   607681 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 225568 100       491913 return 0 if $_[0] eq $_[1];
81 200424         232364 my $can_rely_on_native = 0;
82 200424 100 100     465696 if ($_[0] =~ /\./ || $_[1] =~ /\./) {
83             # if one is a float, both must be, otherwise perl gets it wrong (see test)
84 200368         282842 for ($_[0], $_[1]){
85 400736 100       755779 $_ .= ".0" unless /\./;
86             }
87 200368 100       543953 return 1 if $_[0] - $_[1] > 1;
88 170928 100       359407 return -1 if $_[0] - $_[1] < -1;
89             } else {
90 56         88 $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 140789         178507 my $native = $_[0] <=> $_[1];
95 140789 100 66     240746 return $native if $can_rely_on_native && $native != 0;
96 140733         270752 $_[0] =~ s/^/0/ while index($_[0],".") < index($_[1],".");
97 140733         250501 $_[1] =~ s/^/0/ while index($_[1],".") < index($_[0],".");
98 140733         476934 $_[0] cmp $_[1];
99             }
100              
101             =head2 _bigfloatge ( $l, $r )
102              
103             Same for ge
104              
105             =cut
106             sub _bigfloatge ($$) {
107 153881     153881   226685 _bigfloatcmp($_[0],$_[1]) >= 0;
108             }
109              
110             =head2 _bigfloatgt ( $l, $r )
111              
112             Same for gt
113              
114             =cut
115             sub _bigfloatgt ($$) {
116 23052     23052   45649 _bigfloatcmp($_[0],$_[1]) > 0;
117             }
118              
119             =head2 _bigfloatle ( $l, $r )
120              
121             Same for lt
122              
123             =cut
124             sub _bigfloatle ($$) {
125 8000     8000   14742 _bigfloatcmp($_[0],$_[1]) <= 0;
126             }
127              
128             =head2 _bigfloatlt ( $l, $r )
129              
130             Same for lt
131              
132             =cut
133             sub _bigfloatlt ($$) {
134 39672     39672   62285 _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   2963 my($l,$r) = @_;
144 920 100       2707 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   215 my($l,$r) = @_;
154 43 100       219 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   55593 my($x) = @_;
169 355         463 my $r;
170 355         1516 require Config;
171 355   50     3172 my $nvsize = $Config::Config{nvsize} || 8;
172 355         1127 my $lom = 2*$nvsize; # "length of mantissa": nextup needs more digits
173 355         467 NORMALIZE: while () {
174 714         2981 my $sprintf = "%." . $lom . "f";
175 714         4707 $r = sprintf $sprintf, $x;
176 714 100       2234 if ($r =~ /\.\d+0$/) {
177 355         666 last NORMALIZE;
178             } else {
179 359         568 $lom *= 2;
180             }
181             }
182 355         2018 $r =~ s/(\d)0+$/$1/;
183 355         842 return $r;
184             }
185             sub _increase_a_bit ($;$) {
186 179     179   1011 my($l,$r) = @_;
187 179 50       422 unless (defined $l) {
188 0         0 die "Alert: _increase_a_bit called with undefined first argument";
189             }
190 179 100       615 if (defined $r){
191 3 50       18 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         5413 $r = _my_sprintf_float(Data::Float::nextup($l));
198             }
199 179         362 my $ret;
200 179 50       636 if ($l == $r) {
201             } else {
202             # native try
203 179         605 my $try = _my_sprintf_float(($l+$r)/2);
204 179 100 100     533 if (_bigfloatlt($l,$try) && _bigfloatlt($try,$r) ) {
205 31         67 $ret = $try;
206             }
207             }
208 179 100       532 return $ret if $ret;
209 148         408 return _increase_a_bit_tail($l,$r);
210             }
211             sub _increase_a_bit_tail ($$) {
212 148     148   397 my($l,$r) = @_;
213 148         229 my $ret;
214 148         327 for ($l, $r){
215 296 100       1026 $_ .= ".0" unless /\./;
216             }
217 148         450 $l =~ s/^/0/ while index($l,".") < index($r,".");
218 148         383 $r =~ s/^/0/ while index($r,".") < index($l,".");
219 148         1338 $l .= "0" while length($l) < length($r);
220 148         403 $r .= "0" while length($r) < length($l);
221 148         225 my $diffdigit;
222 148         452 DIG: for (my $i = 0; $i < length($l); $i++) {
223 2637 100       5881 if (substr($l,$i,1) ne substr($r,$i,1)) {
224 148         239 $diffdigit = $i;
225 148         306 last DIG;
226             }
227             }
228 148         281 $ret = substr($l,0,$diffdigit);
229 148         320 my $sl = substr($l,$diffdigit); # significant l
230 148         250 my $sr = substr($r,$diffdigit);
231 148 100       477 if ($ret =~ /\./) {
232 147         280 $sl .= ".0";
233 147         203 $sr .= ".0";
234             }
235 148         254 my $srlength = length $sr;
236 148         280 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         578 my $fformat = sprintf "%%0%d.%df", 1+$srlength, $srmantissa;
240 148         1286 my $appe = sprintf $fformat, ($sl+$sr)/2;
241 148         981 $appe =~ s/(\d)0+$/$1/;
242 148 100       499 if ($ret =~ /\./) {
243 147         431 $appe =~ s/\.//;
244             }
245 148         382 $ret .= $appe;
246 148         209 CHOP: while () {
247 2212         3759 my $try = substr($ret,0,length($ret)-1);
248 2212 100 66     3182 if (_bigfloatlt($l,$try) && _bigfloatlt($try,$r)) {
249 2064         3772 $ret = $try;
250             } else {
251 148         282 last CHOP;
252             }
253             }
254 148         735 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: