1 package Math
::BigFloat
;
5 use Exporter
; # just for use to be happy
10 '+' => sub {new Math
::BigFloat
&fadd
},
11 '-' => sub {new Math
::BigFloat
12 $_[2]? fsub
($_[1],${$_[0]}) : fsub
(${$_[0]},$_[1])},
13 '<=>' => sub {$_[2]? fcmp
($_[1],${$_[0]}) : fcmp
(${$_[0]},$_[1])},
14 'cmp' => sub {$_[2]?
($_[1] cmp ${$_[0]}) : (${$_[0]} cmp $_[1])},
15 '*' => sub {new Math
::BigFloat
&fmul
},
16 '/' => sub {new Math
::BigFloat
17 $_[2]?
scalar fdiv
($_[1],${$_[0]}) :
18 scalar fdiv
(${$_[0]},$_[1])},
19 '%' => sub {new Math
::BigFloat
20 $_[2]?
scalar fmod
($_[1],${$_[0]}) :
21 scalar fmod
(${$_[0]},$_[1])},
22 'neg' => sub {new Math
::BigFloat
&fneg
},
23 'abs' => sub {new Math
::BigFloat
&fabs
},
27 0+ numify) # Order of arguments unsignificant
32 my ($foo) = fnorm
(shift);
36 sub numify
{ 0 + "${$_[0]}" } # Not needed, additional overhead
37 # comparing to direct compilation based on
42 my $minus = ($n =~ s/^([+-])// && $1 eq '-');
54 } elsif (abs($e) < $ln) {
55 substr($n, $ln + $e, 0) = '.';
57 $n = '.' . ("0" x
(abs($e) - $ln)) . $n;
62 # 1 while $n =~ s/(.*\d)(\d\d\d)/$1,$2/;
69 # Rounding modes one of 'even', 'odd', '+inf', '-inf', 'zero' or 'trunc'.
73 sub fadd
; sub fsub
; sub fmul
; sub fdiv
;
74 sub fneg
; sub fabs
; sub fcmp
;
75 sub fround
; sub ffround
;
78 # Convert a number to canonical string form.
79 # Takes something that looks like a number and converts it to
80 # the form /^[+-]\d+E[+-]\d+$/.
81 sub fnorm
{ #(string) return fnum_str
83 s/\s+//g; # strip white space
84 no warnings
; # $4 and $5 below might legitimately be undefined
85 if (/^([+-]?)(\d*)(\.(\d*))?([Ee]([+-]?\d+))?$/ && "$2$4" ne '') {
86 &norm
(($1 ?
"$1$2$4" : "+$2$4"),(($4 ne '') ?
$6-length($4) : $6));
92 # normalize number -- for internal use
93 sub norm
{ #(mantissa, exponent) return fnum_str
95 $exp = 0 unless defined $exp;
99 s/^([+-])0+/$1/; # strip leading zeros
100 if (length($_) == 1) {
103 $exp += length($1) if (s/(0+)$//); # strip trailing zeros
104 sprintf("%sE%+ld", $_, $exp);
110 sub fneg
{ #(fnum_str) return fnum_str
111 local($_) = fnorm
($_[$[]);
112 vec($_,0,8) ^= ord('+') ^ ord('-') unless $_ eq '+0E+0'; # flip sign
118 sub fabs
{ #(fnum_str) return fnum_str
119 local($_) = fnorm
($_[$[]);
125 sub fmul
{ #(fnum_str, fnum_str) return fnum_str
126 local($x,$y) = (fnorm
($_[$[]),fnorm
($_[$[+1]));
127 if ($x eq 'NaN' || $y eq 'NaN') {
130 local($xm,$xe) = split('E',$x);
131 local($ym,$ye) = split('E',$y);
132 &norm
(Math
::BigInt
::bmul
($xm,$ym),$xe+$ye);
137 sub fadd
{ #(fnum_str, fnum_str) return fnum_str
138 local($x,$y) = (fnorm
($_[$[]),fnorm
($_[$[+1]));
139 if ($x eq 'NaN' || $y eq 'NaN') {
142 local($xm,$xe) = split('E',$x);
143 local($ym,$ye) = split('E',$y);
144 ($xm,$xe,$ym,$ye) = ($ym,$ye,$xm,$xe) if ($xe < $ye);
145 &norm
(Math
::BigInt
::badd
($ym,$xm.('0' x
($xe-$ye))),$ye);
150 sub fsub
{ #(fnum_str, fnum_str) return fnum_str
151 fadd
($_[$[],fneg
($_[$[+1]));
155 # args are dividend, divisor, scale (optional)
156 # result has at most max(scale, length(dividend), length(divisor)) digits
157 sub fdiv
#(fnum_str, fnum_str[,scale]) return fnum_str
159 local($x,$y,$scale) = (fnorm
($_[$[]),fnorm
($_[$[+1]),$_[$[+2]);
160 if ($x eq 'NaN' || $y eq 'NaN' || $y eq '+0E+0') {
163 local($xm,$xe) = split('E',$x);
164 local($ym,$ye) = split('E',$y);
165 $scale = $div_scale if (!$scale);
166 $scale = length($xm)-1 if (length($xm)-1 > $scale);
167 $scale = length($ym)-1 if (length($ym)-1 > $scale);
168 $scale = $scale + length($ym) - length($xm);
169 &norm
(&round
(Math
::BigInt
::bdiv
($xm.('0' x
$scale),$ym),
170 Math
::BigInt
::babs
($ym)),
176 # args are dividend, divisor
177 sub fmod
#(fnum_str, fnum_str) return fnum_str
179 local($x,$y) = (fnorm
($_[$[]),fnorm
($_[$[+1]));
180 if ($x eq 'NaN' || $y eq 'NaN' || $y eq '+0E+0') {
183 local($xm,$xe) = split('E',$x);
184 local($ym,$ye) = split('E',$y);
187 $ym .= ('0' x
($ye-$xe));
191 $xm .= ('0' x
($xe-$ye));
193 &norm
(Math
::BigInt
::bmod
($xm,$ym));
196 # round int $q based on fraction $r/$base using $rnd_mode
197 sub round
{ #(int_str, int_str, int_str) return int_str
198 local($q,$r,$base) = @_;
199 if ($q eq 'NaN' || $r eq 'NaN') {
201 } elsif ($rnd_mode eq 'trunc') {
204 local($cmp) = Math
::BigInt
::bcmp
(Math
::BigInt
::bmul
($r,'+2'),$base);
207 ($rnd_mode eq 'zero' ) ||
208 ($rnd_mode eq '-inf' && (substr($q,$[,1) eq '+')) ||
209 ($rnd_mode eq '+inf' && (substr($q,$[,1) eq '-')) ||
210 ($rnd_mode eq 'even' && $q =~ /[24680]$/ ) ||
211 ($rnd_mode eq 'odd' && $q =~ /[13579]$/ ) )
216 Math
::BigInt
::badd
($q, ((substr($q,$[,1) eq '-') ?
'-1' : '+1'));
222 # round the mantissa of $x to $scale digits
223 sub fround
{ #(fnum_str, scale) return fnum_str
224 local($x,$scale) = (fnorm
($_[$[]),$_[$[+1]);
225 if ($x eq 'NaN' || $scale <= 0) {
228 local($xm,$xe) = split('E',$x);
229 if (length($xm)-1 <= $scale) {
232 &norm
(&round
(substr($xm,$[,$scale+1),
233 "+0".substr($xm,$[+$scale+1),"+1"."0" x
length(substr($xm,$[+$scale+1))),
234 $xe+length($xm)-$scale-1);
239 # round $x at the 10 to the $scale digit place
240 sub ffround
{ #(fnum_str, scale) return fnum_str
241 local($x,$scale) = (fnorm
($_[$[]),$_[$[+1]);
245 local($xm,$xe) = split('E',$x);
249 $xe = length($xm)+$xe-$scale;
253 # The first substr preserves the sign, passing a non-
254 # normalized "-0" to &round when rounding -0.006 (for
255 # example), purely so &round won't lose the sign.
256 &norm
(&round
(substr($xm,$[,1).'0',
257 "+0".substr($xm,$[+1),
258 "+1"."0" x
length(substr($xm,$[+1))), $scale);
260 &norm
(&round
(substr($xm,$[,$xe),
261 "+0".substr($xm,$[+$xe),
262 "+1"."0" x
length(substr($xm,$[+$xe))), $scale);
268 # compare 2 values returns one of undef, <0, =0, >0
269 # returns undef if either or both input value are not numbers
270 sub fcmp
#(fnum_str, fnum_str) return cond_code
272 local($x, $y) = (fnorm
($_[$[]),fnorm
($_[$[+1]));
273 if ($x eq "NaN" || $y eq "NaN") {
276 local($xm,$xe,$ym,$ye) = split('E', $x."E$y");
277 if ($xm eq '+0' || $ym eq '+0') {
280 if ( $xe < $ye ) # adjust the exponents to be equal
282 $ym .= '0' x
($ye - $xe);
285 elsif ( $ye < $xe ) # same here
287 $xm .= '0' x
($xe - $ye);
290 return Math
::BigInt
::cmp($xm,$ym);
294 # square root by Newtons method.
295 sub fsqrt
{ #(fnum_str[, scale]) return fnum_str
296 local($x, $scale) = (fnorm
($_[$[]), $_[$[+1]);
297 if ($x eq 'NaN' || $x =~ /^-/) {
299 } elsif ($x eq '+0E+0') {
302 local($xm, $xe) = split('E',$x);
303 $scale = $div_scale if (!$scale);
304 $scale = length($xm)-1 if ($scale < length($xm)-1);
305 local($gs, $guess) = (1, sprintf("1E%+d", (length($xm)+$xe-1)/2));
306 while ($gs < 2*$scale) {
307 $guess = fmul
(fadd
($guess,fdiv
($x,$guess,$gs*2)),".5");
310 new Math
::BigFloat
&fround
($guess, $scale);
319 Math::BigFloat - Arbitrary length float math package
324 $f = Math::BigFloat->new($string);
326 $f->fadd(NSTR) return NSTR addition
327 $f->fsub(NSTR) return NSTR subtraction
328 $f->fmul(NSTR) return NSTR multiplication
329 $f->fdiv(NSTR[,SCALE]) returns NSTR division to SCALE places
330 $f->fmod(NSTR) returns NSTR modular remainder
331 $f->fneg() return NSTR negation
332 $f->fabs() return NSTR absolute value
333 $f->fcmp(NSTR) return CODE compare undef,<0,=0,>0
334 $f->fround(SCALE) return NSTR round to SCALE digits
335 $f->ffround(SCALE) return NSTR round at SCALEth place
336 $f->fnorm() return (NSTR) normalize
337 $f->fsqrt([SCALE]) return NSTR sqrt to SCALE places
341 All basic math operations are overloaded if you declare your big
344 $float = new Math::BigFloat "2.123123123123123123123123123123123";
350 canonical strings have the form /[+-]\d+E[+-]\d+/ . Input values can
351 have embedded whitespace.
353 =item Error returns 'NaN'
355 An input parameter was "Not a Number" or divide by zero or sqrt of
358 =item Division is computed to
360 C<max($Math::BigFloat::div_scale,length(dividend)+length(divisor))>
362 Also used for default sqrt scale.
364 =item Rounding is performed
366 according to the value of
367 C<$Math::BigFloat::rnd_mode>:
369 trunc truncate the value
371 +inf round towards +infinity (round up)
372 -inf round towards -infinity (round down)
373 even round to the nearest, .5 to the even digit
374 odd round to the nearest, .5 to the odd digit
376 The default is C<even> rounding.
382 The current version of this module is a preliminary version of the
383 real thing that is currently (as of perl5.002) under development.
385 The printf subroutine does not use the value of
386 C<$Math::BigFloat::rnd_mode> when rounding values for printing.
387 Consequently, the way to print rounded values is
388 to specify the number of digits both as an
389 argument to C<ffround> and in the C<%f> printf string,
392 printf "%.3f\n", $bigfloat->ffround(-3);
397 Patches by John Peacock Apr 2001