File Coverage

blib/lib/Math/Ryu.pm
Criterion Covered Total %
statement 102 137 74.4
branch 60 84 71.4
condition 12 24 50.0
subroutine 20 26 76.9
pod 0 13 0.0
total 194 284 68.3


line stmt bran cond sub pod time code
1             ## This file generated by InlineX::C2XS (version 0.26) using Inline::C (version 0.79_001)
2             package Math::Ryu;
3 8     8   218965 use warnings;
  8         15  
  8         514  
4 8     8   45 use strict;
  8         57  
  8         245  
5 8     8   44 use Config;
  8         14  
  8         696  
6              
7             BEGIN {
8             # In this BEGIN{} block we check for the presence of a
9             # perl bug that can set the POK flag when it should not.
10 8     8   56 use B qw(svref_2object);
  8         21  
  8         1667  
11              
12 8 50   8   79 if($] < 5.035010) { # TODO - Check if this value can be reduced (to around 5.02 ?)
13 0         0 my %flags;
14             {
15 8     8   65 no strict 'refs';
  8         19  
  8         2234  
  0         0  
16 0         0 for my $flag (qw(
17             SVf_IOK
18             SVf_NOK
19             SVf_POK
20             SVp_IOK
21             SVp_NOK
22             SVp_POK
23             )) {
24 0 0       0 if (defined &{'B::'.$flag}) {
  0         0  
25 0         0 $flags{$flag} = &{'B::'.$flag};
  0         0  
26             }
27             }
28             }
29              
30 0         0 my $test_nv = 1.3;
31 0         0 my $buggery = "$test_nv";
32 0         0 my $flags = B::svref_2object(\$test_nv)->FLAGS;
33 0         0 my $fstr = join ' ', sort grep $flags & $flags{$_}, keys %flags;
34              
35 0 0       0 if($fstr =~ /SVf_POK/) {
36 0         0 $Math::Ryu::PV_NV_BUG = 1;
37             }
38             else {
39 0         0 $Math::Ryu::PV_NV_BUG = 0;
40             }
41             } # close if{} block
42             else {
43 8         1156 $Math::Ryu::PV_NV_BUG = 0;
44             }
45             }; # close BEGIN{} block
46              
47             BEGIN {
48 8 50 0 8   880 if($Config{nvsize} == 8) { $::max_dig = 17 }
  8 0       607  
    0          
49 0         0 elsif($Config{nvtype} eq '__float128') { $::max_dig = 36 }
50             elsif(defined($Config{longdblkind})
51 0         0 && $Config{longdblkind} < 3) { $::max_dig = 36 }
52 0         0 else { $::max_dig = 21 }
53              
54             }; # close BEGIN{} block
55              
56 8     8   66 use constant PV_NV_BUG => $Math::Ryu::PV_NV_BUG;
  8         32  
  8         1272  
57 8     8   63 use constant IVSIZE => $Config{ivsize};
  8         16  
  8         1357  
58 8     8   74 use constant MAX_DEC_DIG => $::max_dig; # set in second BEGIN{} block
  8         17  
  8         666  
59 8 50       691 use constant RYU_MAX_INT => $Config{ivsize} == 4 ? 4294967295
60 8     8   51 : 18446744073709551615;
  8         15  
61 8 50       18408 use constant RYU_MIN_INT => $Config{ivsize} == 4 ? -2147483648
62 8     8   187 : -9223372036854775808;
  8         16  
63              
64             require Exporter;
65             *import = \&Exporter::import;
66             require DynaLoader;
67              
68             our $VERSION = '1.09';
69              
70             DynaLoader::bootstrap Math::Ryu $VERSION;
71              
72             my @tagged = qw(
73             d2s ld2s q2s nv2s fx2s
74             is_NV
75             pn pnv pany sn snv sany
76             spanyf
77             n2s
78             s2d
79             fmtpy fmtpy_pp
80             ryu_lln ryu_refcnt ryu_SvIOK ryu_SvNOK ryu_SvPOK ryu_SvIOKp
81             );
82              
83             @Math::Ryu::EXPORT = ();
84             @Math::Ryu::EXPORT_OK = @tagged;
85             %Math::Ryu::EXPORT_TAGS = (all => \@tagged);
86              
87             my $double_inf = 2 ** 1500;
88             my $double_nan = $double_inf / $double_inf;
89              
90             # If $Math::Ryu::PERL_INFNAN is set to 0, then the string "inf" represents
91             # positive infinity, the string "-inf" represents negative infinity, and the
92             # string "nan" represents a NaN (not a number).
93             # If $Math::Ryu::PERL_INFNAN is set to a true value then the strings will be
94             # whatever perl uses to signify positive infinity, negative infinity and nan.
95              
96             $Math::Ryu::PERL_INFNAN = 0; # Default setting. You can override this
97             # setting in your code.
98              
99             my $pinf = 1e5000;
100             my $ninf = -$pinf;
101             my $nanv = $pinf / $pinf;
102             $Math::Ryu::pinfstr = "$pinf";
103             $Math::Ryu::ninfstr = "$ninf";
104             $Math::Ryu::nanvstr = "$nanv";
105              
106 8     8 0 3236 sub dl_load_flags {0} # Prevent DynaLoader from complaining and croaking
107              
108             sub nv2s {
109 2790     2790 0 3444952 my $nv = shift;
110 2790         51187 return fmtpy( d2s($nv)) if MAX_DEC_DIG == 17;
111 0         0 return fmtpy(ld2s($nv)) if MAX_DEC_DIG == 21;
112 0         0 return fmtpy( q2s($nv));
113             }
114              
115             sub n2s {
116 8     8 0 255409 my $arg = shift;
117 8 100       53 die "The n2s() function does not accept ", ref($arg), " references"
118             if ref($arg);
119 7 100       32 if(!ryu_SvPOK($arg)) {
120 3 50       11 return $arg if ryu_SvIOK($arg);
121 3 50       6 return _from_NV($arg) if _NV_fits_IV($arg);
122             }
123 7 100       38 return nv2s($arg) if ryu_SvNOK($arg);
124             # When this sub is called by pany() or sany(), it
125             # will have returned before reaching here.
126             # $arg is neither integer nor float nor reference.
127             # If the numified $arg fits into an IV, return the
128             # stringification of that value.
129             # Else, return nv2s($arg), which will coerce $arg
130             # to an NV.
131 3         80 my $ret = $arg + 0;
132 3 50       19 return "$ret" if ryu_SvIOK($ret);
133             #return nv2s($ret) if ryu_SvNOK($ret);
134 3         10 return nv2s($arg);
135             }
136              
137             sub fmtpy_pp {
138             # Pure perl rendition of the fmtpy function. Not exported.
139             # The given argument will be either 'Infinity', '-Infinity', 'NaN'
140             # or a finite value of the form "mantissaEexponent".
141             # The mantissa portion will include a decimal point (with that decimal
142             # point being the second character in the mantissa) unless the
143             # mantissa consists of only one decimal significant decimal digit,
144             # in which case there is no decimal point and the mantissa consists
145             # solely of that digit.
146              
147 14     14 0 70 my $s = shift;
148 14         28 my $sign = '';
149 14         26 my $bitpos = 0;
150              
151 14 100       71 $sign = '-' if $s =~ s/^\-//;
152              
153 14 100       76 $bitpos = 1 if substr($s, 1, 1) eq '.'; # else there isn't a decimal point.
154              
155 14 100       38 if($bitpos) {
156             # Mantissa's second character is the decimal point.
157             # Split into mantissa and exponent
158 4         25 my @parts = split /E/i, $s;
159 4 50 33     30 if($parts[1] > 0 && $parts[1] < MAX_DEC_DIG) {
160             # We want, eg, a value like 1.1E-3 to be returned as "0.0011".
161 4         10 my $zero_pad = $parts[1] - (length($parts[0]) - 2);
162 4 100 66     23 if($zero_pad >= 0 && ($zero_pad + length($parts[0])) < MAX_DEC_DIG + 1 ) {
    50          
163 2         8 substr($parts[0], 1, 1, '');
164 2         25 return $sign . $parts[0] . ('0' x $zero_pad) . '.0';
165             }
166             elsif($zero_pad < 0) {
167             # We want, eg, a value like 1.23625E2 to be returned as "123.625".
168             # relocate the decimal point
169 0         0 substr($parts[0], 1, 1, '');
170 0         0 substr($parts[0], $zero_pad, 0, '.');
171 0         0 return $sign . $parts[0];
172             }
173             }
174              
175             # Return as is, except that we replace the 'E' with 'e', ensuring also
176             # that the exponent is preceded by a '+' or '-' sign, and that
177             # negative exponents consist of at least 2 digits.
178 2 50       11 $s =~ s/e/e\+/i if $parts[1] > 0;
179 2 50 33     14 if ($parts[1] < -4 || $parts[1] >= 0) {
180 2         5 $s =~ s/E0$//i;
181 2 50       25 substr($s, -1, 0, '0') if substr($s, -2, 1) eq '-'; # pad exponent with a leading '0'.
182 2         22 return $sign . lc($s);
183             }
184             # Return, eg 6.25E1 as "0.625"
185 0         0 substr($parts[0], 1, 1, ''); # remove decimal point.
186 0         0 return $sign . '0.' . ('0' x (abs($parts[1]) - 1)) . $parts[0] ;
187             }
188             else {
189             # Return '-inf', 'inf', or 'nan' if (and as) appropriate.
190 10 100       52 if($s =~ /n/i) {
191 6 100       17 if($Math::Ryu::PERL_INFNAN) {
192 3 100       16 return $Math::Ryu::nanvstr if $s =~ /a/i;
193 2 100       12 return $Math::Ryu::ninfstr if $sign;
194 1         8 return $Math::Ryu::pinfstr;
195             }
196 3         24 return $sign . lc(substr($s, 0, 3));
197             }
198              
199             # Append '.0' to the mantissa and return it if the exponent is 0.
200 4 50       12 return $sign . $s . '.0' if $s =~ s/E0$//i;
201 4         17 my @parts = split /E/i, $s;
202              
203             # Return as is, except that we replace the 'E' with 'e', ensuring also
204             # that the exponent is preceded by a '+' or '-' sign, and that
205             # negative exponents consist of at least 2 digits.
206 4 50       24 $s =~ s/e/e\+/i if $parts[1] > 0;
207 4 50 33     15 $s =~ s/e\-/e\-0/i if ($parts[1] < -4 && $parts[1] > -10);
208 4 100 66     43 return $sign . lc($s) if ($parts[1] < -4 || $parts[1] > MAX_DEC_DIG - 2);
209              
210 2 50       5 if($parts[1] >= 0 ) { # $parts[1] is in the range 1..(MAX_DEC_DIG - 2)
211 2         23 return $sign . $parts[0] . (0 x $parts[1]) . '.0';
212             }
213              
214             # Return, eg, 6E-3 as "0.006".
215 0         0 return $sign . '0.' . ('0' x (abs($parts[1]) - 1)) . $parts[0] ;
216             }
217             }
218              
219             sub s2d {
220 9647     9647 0 4969490 die "s2d() is available only to perls whose NV is of type 'double'"
221             unless MAX_DEC_DIG == 17;
222 9647         17726 my $str = shift;
223              
224 9647 100       44456 die("Strings passed to s2d() must \"look like a number\"")
225             unless ryu_lln($str);
226             # For _s2d, we need to remove all leading and trailing whitespace.
227             # If $str contained internal whitespace, then s2d has already died.
228 9645         27344 $str =~ s/\s//g;
229              
230             # _s2d doesn't handle inf/nan, so we attend to that first.
231             # However, if perl recognizes these strings as numeric, then it's
232             # not really necessary to assign them using s2d.
233             # And if perl does NOT recognize these strings as numeric, then s2d
234             # is simply going to croak.
235             # Anyway, here they are:
236 9645 100       39605 return $double_inf if $str =~ /^\+?inf/i;
237 9499 100       25303 return -$double_inf if $str =~ /^\-inf/i;
238 9462 100       33234 return $double_nan if $str =~ /^(\-|\+)?nan/i;
239              
240 9460         93126 return _s2d($str);
241             }
242              
243             sub pn {
244 0     0 0 0 my $arg = shift;
245 0         0 print n2s($arg);
246             }
247              
248             sub sn {
249 0     0 0 0 my $arg = shift;
250 0         0 print n2s($arg), "\n";
251             }
252              
253             sub pnv {
254 0     0 0 0 my $nv = shift;
255 0         0 print nv2s($nv);
256             }
257              
258             sub snv {
259 0     0 0 0 my $nv = shift;
260 0         0 print nv2s($nv), "\n";
261             }
262              
263             sub pany { # "p"rint "any"
264 0     0 0 0 print spanyf(@_)
265             }
266              
267             sub sany {
268 0     0 0 0 print spanyf(@_) . "\n";
269             }
270              
271             sub spanyf {
272             # Prepares the string that pany/sany will print.
273 13     13 0 428886 my $ret = '';
274              
275 13         65 for my $arg (@_) {
276 39 100       301 if(is_NV($arg)) {
277 8 100       16 if(_NV_fits_IV($arg)) { $ret .= _from_NV($arg) } # nv2s($arg) would append a ".0"
  3         33  
278 5         13 else { $ret .= nv2s($arg) }
279             }
280             else {
281 31         83 $ret .= "$arg";
282             }
283             }
284 13         231 return $ret;
285             }
286              
287             sub _NV_fits_IV {
288             # Called only when the argument is an NV
289 11     11   14 my $nv = shift;
290 11 100       44 return 0 if $nv != int($nv);
291 8 100 66     34 return 1 if ( $nv <= RYU_MAX_INT && $nv >= RYU_MIN_INT );
292 5         11 return 0;
293             }
294              
295             sub is_NV {
296 55     55 0 572430 my $arg = shift;
297              
298 55         112 return 1
299             if(PV_NV_BUG && ryu_SvPOK($arg) && ryu_SvNOK($arg) && !ryu_SvIOKp($arg));
300              
301 55 100       312 if(ryu_lln($arg)) { # Wraps the perl API function looks_like_number(),
302             # which differs from Scalar::Util::looks_like_number().
303 26 100 100     176 return 0
304             if(ryu_SvPOK($arg) || ryu_SvIOK($arg));
305              
306             # At this point we know that $arg looks like a number
307             # and neither its POK flag nor its IOK flag is set.
308             # It must be an NV.
309 11         39 return 1;
310             }
311 29         90 return 0;
312             }
313              
314             1;