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 9     9   33 use strict;
  9         9  
  9         217  
5 9     9   4449 use Data::Float qw(nextup);
  9         59352  
  9         1181  
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 9     9   3192 use version; our $VERSION = qv('0.0.8');
  9         12010  
  9         43  
28              
29 9     9   624 use Exporter;
  9         10  
  9         438  
30 9     9   36 use base qw(Exporter);
  9         9  
  9         9208  
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 268053 50 33 268053   693201 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 268053 100       490123 return 0 if $_[0] eq $_[1];
81 238145         161500 my $can_rely_on_native = 0;
82 238145 100 100     460050 if ($_[0] =~ /\./ || $_[1] =~ /\./) {
83             # if one is a float, both must be, otherwise perl gets it wrong (see test)
84 238089         243269 for ($_[0], $_[1]){
85 476178 100       745254 $_ .= ".0" unless /\./;
86             }
87 238089 100       514076 return 1 if $_[0] - $_[1] > 1;
88 203401 100       367038 return -1 if $_[0] - $_[1] < -1;
89             } else {
90 56         44 $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 169050         128305 my $native = $_[0] <=> $_[1];
95 169050 100 66     237233 return $native if $can_rely_on_native && $native != 0;
96 168994         268064 $_[0] =~ s/^/0/ while index($_[0],".") < index($_[1],".");
97 168994         238942 $_[1] =~ s/^/0/ while index($_[1],".") < index($_[0],".");
98 168994         529916 $_[0] cmp $_[1];
99             }
100              
101             =head2 _bigfloatge ( $l, $r )
102              
103             Same for ge
104              
105             =cut
106             sub _bigfloatge ($$) {
107 184176     184176   198762 _bigfloatcmp($_[0],$_[1]) >= 0;
108             }
109              
110             =head2 _bigfloatgt ( $l, $r )
111              
112             Same for gt
113              
114             =cut
115             sub _bigfloatgt ($$) {
116 27315     27315   36511 _bigfloatcmp($_[0],$_[1]) > 0;
117             }
118              
119             =head2 _bigfloatle ( $l, $r )
120              
121             Same for lt
122              
123             =cut
124             sub _bigfloatle ($$) {
125 7368     7368   9478 _bigfloatcmp($_[0],$_[1]) <= 0;
126             }
127              
128             =head2 _bigfloatlt ( $l, $r )
129              
130             Same for lt
131              
132             =cut
133             sub _bigfloatlt ($$) {
134 48053     48053   58011 _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 1096     1096   1848 my($l,$r) = @_;
144 1096 100       2052 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 45     45   98 my($l,$r) = @_;
154 45 100       77 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 425     425   41392 my($x) = @_;
169 425         341 my $r;
170 425         1353 require Config;
171 425   50     2854 my $nvsize = $Config::Config{nvsize} || 8;
172 425         772 my $lom = 2*$nvsize; # "length of mantissa": nextup needs more digits
173 425         322 NORMALIZE: while () {
174 873         942 my $sprintf = "%." . $lom . "f";
175 873         4773 $r = sprintf $sprintf, $x;
176 873 100       2080 if ($r =~ /\.\d+0$/) {
177 425         475 last NORMALIZE;
178             } else {
179 448         485 $lom *= 2;
180             }
181             }
182 425         1722 $r =~ s/(\d)0+$/$1/;
183 425         715 return $r;
184             }
185             sub _increase_a_bit ($;$) {
186 214     214   860 my($l,$r) = @_;
187 214 50       364 unless (defined $l) {
188 0         0 die "Alert: _increase_a_bit called with undefined first argument";
189             }
190 214 100       405 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 211         5173 $r = _my_sprintf_float(Data::Float::nextup($l));
198             }
199 214         219 my $ret;
200 214 50       555 if ($l == $r) {
201             } else {
202             # native try
203 214         479 my $try = _my_sprintf_float(($l+$r)/2);
204 214 100 100     398 if (_bigfloatlt($l,$try) && _bigfloatlt($try,$r) ) {
205 37         51 $ret = $try;
206             }
207             }
208 214 100       495 return $ret if $ret;
209 177         362 return _increase_a_bit_tail($l,$r);
210             }
211             sub _increase_a_bit_tail ($$) {
212 177     177   279 my($l,$r) = @_;
213 177         188 my $ret;
214 177         263 for ($l, $r){
215 354 100       764 $_ .= ".0" unless /\./;
216             }
217 177         465 $l =~ s/^/0/ while index($l,".") < index($r,".");
218 177         420 $r =~ s/^/0/ while index($r,".") < index($l,".");
219 177         1813 $l .= "0" while length($l) < length($r);
220 177         385 $r .= "0" while length($r) < length($l);
221 177         147 my $diffdigit;
222 177         427 DIG: for (my $i = 0; $i < length($l); $i++) {
223 3166 100       5972 if (substr($l,$i,1) ne substr($r,$i,1)) {
224 177         177 $diffdigit = $i;
225 177         213 last DIG;
226             }
227             }
228 177         250 $ret = substr($l,0,$diffdigit);
229 177         245 my $sl = substr($l,$diffdigit); # significant l
230 177         196 my $sr = substr($r,$diffdigit);
231 177 100       557 if ($ret =~ /\./) {
232 176         164 $sl .= ".0";
233 176         201 $sr .= ".0";
234             }
235 177         194 my $srlength = length $sr;
236 177         244 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 177         560 my $fformat = sprintf "%%0%d.%df", 1+$srlength, $srmantissa;
240 177         1306 my $appe = sprintf $fformat, ($sl+$sr)/2;
241 177         2113 $appe =~ s/(\d)0+$/$1/;
242 177 100       525 if ($ret =~ /\./) {
243 176         333 $appe =~ s/\.//;
244             }
245 177         915 $ret .= $appe;
246 177         220 CHOP: while () {
247 2783         3301 my $try = substr($ret,0,length($ret)-1);
248 2783 100 66     2795 if (_bigfloatlt($l,$try) && _bigfloatlt($try,$r)) {
249 2606         3041 $ret = $try;
250             } else {
251 177         286 last CHOP;
252             }
253             }
254 177         603 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: