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   57 use strict;
  8         18  
  8         314  
5 8     8   3704 use Data::Float qw(nextup);
  8         71658  
  8         1310  
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   2697 use version; our $VERSION = qv('0.0.8');
  8         14086  
  8         51  
28              
29 8     8   823 use Exporter;
  8         24  
  8         420  
30 8     8   56 use base qw(Exporter);
  8         19  
  8         13011  
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 206711 50 33 206711   871637 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 206711 100       693571 return 0 if $_[0] eq $_[1];
81 183993         380541 my $can_rely_on_native = 0;
82 183993 100 100     846007 if ($_[0] =~ /\./ || $_[1] =~ /\./) {
83             # if one is a float, both must be, otherwise perl gets it wrong (see test)
84 183937         412255 for ($_[0], $_[1]){
85 367874 100       1260661 $_ .= ".0" unless /\./;
86             }
87 183937 100       830647 return 1 if $_[0] - $_[1] > 1;
88 156949 100       579698 return -1 if $_[0] - $_[1] < -1;
89             } else {
90 56         135 $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 128879         275042 my $native = $_[0] <=> $_[1];
95 128879 100 66     362543 return $native if $can_rely_on_native && $native != 0;
96 128823         440118 $_[0] =~ s/^/0/ while index($_[0],".") < index($_[1],".");
97 128823         384742 $_[1] =~ s/^/0/ while index($_[1],".") < index($_[0],".");
98 128823         842464 $_[0] cmp $_[1];
99             }
100              
101             =head2 _bigfloatge ( $l, $r )
102              
103             Same for ge
104              
105             =cut
106             sub _bigfloatge ($$) {
107 140101     140101   332771 _bigfloatcmp($_[0],$_[1]) >= 0;
108             }
109              
110             =head2 _bigfloatgt ( $l, $r )
111              
112             Same for gt
113              
114             =cut
115             sub _bigfloatgt ($$) {
116 20684     20684   65418 _bigfloatcmp($_[0],$_[1]) > 0;
117             }
118              
119             =head2 _bigfloatle ( $l, $r )
120              
121             Same for lt
122              
123             =cut
124             sub _bigfloatle ($$) {
125 7596     7596   25702 _bigfloatcmp($_[0],$_[1]) <= 0;
126             }
127              
128             =head2 _bigfloatlt ( $l, $r )
129              
130             Same for lt
131              
132             =cut
133             sub _bigfloatlt ($$) {
134 37367     37367   119437 _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   4198 my($l,$r) = @_;
144 920 100       4123 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   168 my($l,$r) = @_;
154 43 100       187 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   86154 my($x) = @_;
169 355         813 my $r;
170 355         2512 require Config;
171 355   50     4641 my $nvsize = $Config::Config{nvsize} || 8;
172 355         1531 my $lom = 2*$nvsize; # "length of mantissa": nextup needs more digits
173 355         940 NORMALIZE: while () {
174 729         2033 my $sprintf = "%." . $lom . "f";
175 729         6853 $r = sprintf $sprintf, $x;
176 729 100       3704 if ($r =~ /\.\d+0$/) {
177 355         1272 last NORMALIZE;
178             } else {
179 374         921 $lom *= 2;
180             }
181             }
182 355         3100 $r =~ s/(\d)0+$/$1/;
183 355         1502 return $r;
184             }
185             sub _increase_a_bit ($;$) {
186 179     179   1434 my($l,$r) = @_;
187 179 50       697 unless (defined $l) {
188 0         0 die "Alert: _increase_a_bit called with undefined first argument";
189             }
190 179 100       655 if (defined $r){
191 3 50       20 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         7989 $r = _my_sprintf_float(Data::Float::nextup($l));
198             }
199 179         510 my $ret;
200 179 50       1082 if ($l == $r) {
201             } else {
202             # native try
203 179         817 my $try = _my_sprintf_float(($l+$r)/2);
204 179 100 100     805 if (_bigfloatlt($l,$try) && _bigfloatlt($try,$r) ) {
205 31         84 $ret = $try;
206             }
207             }
208 179 100       882 return $ret if $ret;
209 148         954 return _increase_a_bit_tail($l,$r);
210             }
211             sub _increase_a_bit_tail ($$) {
212 148     148   584 my($l,$r) = @_;
213 148         407 my $ret;
214 148         586 for ($l, $r){
215 296 100       1504 $_ .= ".0" unless /\./;
216             }
217 148         1031 $l =~ s/^/0/ while index($l,".") < index($r,".");
218 148         861 $r =~ s/^/0/ while index($r,".") < index($l,".");
219 148         2186 $l .= "0" while length($l) < length($r);
220 148         805 $r .= "0" while length($r) < length($l);
221 148         349 my $diffdigit;
222 148         792 DIG: for (my $i = 0; $i < length($l); $i++) {
223 2622 100       8645 if (substr($l,$i,1) ne substr($r,$i,1)) {
224 148         350 $diffdigit = $i;
225 148         472 last DIG;
226             }
227             }
228 148         632 $ret = substr($l,0,$diffdigit);
229 148         449 my $sl = substr($l,$diffdigit); # significant l
230 148         491 my $sr = substr($r,$diffdigit);
231 148 100       847 if ($ret =~ /\./) {
232 147         491 $sl .= ".0";
233 147         488 $sr .= ".0";
234             }
235 148         426 my $srlength = length $sr;
236 148         554 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         915 my $fformat = sprintf "%%0%d.%df", 1+$srlength, $srmantissa;
240 148         1870 my $appe = sprintf $fformat, ($sl+$sr)/2;
241 148         1598 $appe =~ s/(\d)0+$/$1/;
242 148 100       1044 if ($ret =~ /\./) {
243 147         776 $appe =~ s/\.//;
244             }
245 148         617 $ret .= $appe;
246 148         435 CHOP: while () {
247 2237         6466 my $try = substr($ret,0,length($ret)-1);
248 2237 100 66     5487 if (_bigfloatlt($l,$try) && _bigfloatlt($try,$r)) {
249 2089         6341 $ret = $try;
250             } else {
251 148         558 last CHOP;
252             }
253             }
254 148         1254 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: