1*0Sstevel@tonic-gatepackage bigfloat; 2*0Sstevel@tonic-gaterequire "bigint.pl"; 3*0Sstevel@tonic-gate# 4*0Sstevel@tonic-gate# This library is no longer being maintained, and is included for backward 5*0Sstevel@tonic-gate# compatibility with Perl 4 programs which may require it. 6*0Sstevel@tonic-gate# 7*0Sstevel@tonic-gate# In particular, this should not be used as an example of modern Perl 8*0Sstevel@tonic-gate# programming techniques. 9*0Sstevel@tonic-gate# 10*0Sstevel@tonic-gate# Suggested alternative: Math::BigFloat 11*0Sstevel@tonic-gate# 12*0Sstevel@tonic-gate# Arbitrary length float math package 13*0Sstevel@tonic-gate# 14*0Sstevel@tonic-gate# by Mark Biggar 15*0Sstevel@tonic-gate# 16*0Sstevel@tonic-gate# number format 17*0Sstevel@tonic-gate# canonical strings have the form /[+-]\d+E[+-]\d+/ 18*0Sstevel@tonic-gate# Input values can have embedded whitespace 19*0Sstevel@tonic-gate# Error returns 20*0Sstevel@tonic-gate# 'NaN' An input parameter was "Not a Number" or 21*0Sstevel@tonic-gate# divide by zero or sqrt of negative number 22*0Sstevel@tonic-gate# Division is computed to 23*0Sstevel@tonic-gate# max($div_scale,length(dividend)+length(divisor)) 24*0Sstevel@tonic-gate# digits by default. 25*0Sstevel@tonic-gate# Also used for default sqrt scale 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate$div_scale = 40; 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate# Rounding modes one of 'even', 'odd', '+inf', '-inf', 'zero' or 'trunc'. 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gate$rnd_mode = 'even'; 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate# bigfloat routines 34*0Sstevel@tonic-gate# 35*0Sstevel@tonic-gate# fadd(NSTR, NSTR) return NSTR addition 36*0Sstevel@tonic-gate# fsub(NSTR, NSTR) return NSTR subtraction 37*0Sstevel@tonic-gate# fmul(NSTR, NSTR) return NSTR multiplication 38*0Sstevel@tonic-gate# fdiv(NSTR, NSTR[,SCALE]) returns NSTR division to SCALE places 39*0Sstevel@tonic-gate# fneg(NSTR) return NSTR negation 40*0Sstevel@tonic-gate# fabs(NSTR) return NSTR absolute value 41*0Sstevel@tonic-gate# fcmp(NSTR,NSTR) return CODE compare undef,<0,=0,>0 42*0Sstevel@tonic-gate# fround(NSTR, SCALE) return NSTR round to SCALE digits 43*0Sstevel@tonic-gate# ffround(NSTR, SCALE) return NSTR round at SCALEth place 44*0Sstevel@tonic-gate# fnorm(NSTR) return (NSTR) normalize 45*0Sstevel@tonic-gate# fsqrt(NSTR[, SCALE]) return NSTR sqrt to SCALE places 46*0Sstevel@tonic-gate 47*0Sstevel@tonic-gate# Convert a number to canonical string form. 48*0Sstevel@tonic-gate# Takes something that looks like a number and converts it to 49*0Sstevel@tonic-gate# the form /^[+-]\d+E[+-]\d+$/. 50*0Sstevel@tonic-gatesub main'fnorm { #(string) return fnum_str 51*0Sstevel@tonic-gate local($_) = @_; 52*0Sstevel@tonic-gate s/\s+//g; # strip white space 53*0Sstevel@tonic-gate if (/^([+-]?)(\d*)(\.(\d*))?([Ee]([+-]?\d+))?$/ 54*0Sstevel@tonic-gate && ($2 ne '' || defined($4))) { 55*0Sstevel@tonic-gate my $x = defined($4) ? $4 : ''; 56*0Sstevel@tonic-gate &norm(($1 ? "$1$2$x" : "+$2$x"), (($x ne '') ? $6-length($x) : $6)); 57*0Sstevel@tonic-gate } else { 58*0Sstevel@tonic-gate 'NaN'; 59*0Sstevel@tonic-gate } 60*0Sstevel@tonic-gate} 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate# normalize number -- for internal use 63*0Sstevel@tonic-gatesub norm { #(mantissa, exponent) return fnum_str 64*0Sstevel@tonic-gate local($_, $exp) = @_; 65*0Sstevel@tonic-gate if ($_ eq 'NaN') { 66*0Sstevel@tonic-gate 'NaN'; 67*0Sstevel@tonic-gate } else { 68*0Sstevel@tonic-gate s/^([+-])0+/$1/; # strip leading zeros 69*0Sstevel@tonic-gate if (length($_) == 1) { 70*0Sstevel@tonic-gate '+0E+0'; 71*0Sstevel@tonic-gate } else { 72*0Sstevel@tonic-gate $exp += length($1) if (s/(0+)$//); # strip trailing zeros 73*0Sstevel@tonic-gate sprintf("%sE%+ld", $_, $exp); 74*0Sstevel@tonic-gate } 75*0Sstevel@tonic-gate } 76*0Sstevel@tonic-gate} 77*0Sstevel@tonic-gate 78*0Sstevel@tonic-gate# negation 79*0Sstevel@tonic-gatesub main'fneg { #(fnum_str) return fnum_str 80*0Sstevel@tonic-gate local($_) = &'fnorm($_[$[]); 81*0Sstevel@tonic-gate vec($_,0,8) ^= ord('+') ^ ord('-') unless $_ eq '+0E+0'; # flip sign 82*0Sstevel@tonic-gate if ( ord("\t") == 9 ) { # ascii 83*0Sstevel@tonic-gate s/^H/N/; 84*0Sstevel@tonic-gate } 85*0Sstevel@tonic-gate else { # ebcdic character set 86*0Sstevel@tonic-gate s/\373/N/; 87*0Sstevel@tonic-gate } 88*0Sstevel@tonic-gate $_; 89*0Sstevel@tonic-gate} 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gate# absolute value 92*0Sstevel@tonic-gatesub main'fabs { #(fnum_str) return fnum_str 93*0Sstevel@tonic-gate local($_) = &'fnorm($_[$[]); 94*0Sstevel@tonic-gate s/^-/+/; # mash sign 95*0Sstevel@tonic-gate $_; 96*0Sstevel@tonic-gate} 97*0Sstevel@tonic-gate 98*0Sstevel@tonic-gate# multiplication 99*0Sstevel@tonic-gatesub main'fmul { #(fnum_str, fnum_str) return fnum_str 100*0Sstevel@tonic-gate local($x,$y) = (&'fnorm($_[$[]),&'fnorm($_[$[+1])); 101*0Sstevel@tonic-gate if ($x eq 'NaN' || $y eq 'NaN') { 102*0Sstevel@tonic-gate 'NaN'; 103*0Sstevel@tonic-gate } else { 104*0Sstevel@tonic-gate local($xm,$xe) = split('E',$x); 105*0Sstevel@tonic-gate local($ym,$ye) = split('E',$y); 106*0Sstevel@tonic-gate &norm(&'bmul($xm,$ym),$xe+$ye); 107*0Sstevel@tonic-gate } 108*0Sstevel@tonic-gate} 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gate# addition 111*0Sstevel@tonic-gatesub main'fadd { #(fnum_str, fnum_str) return fnum_str 112*0Sstevel@tonic-gate local($x,$y) = (&'fnorm($_[$[]),&'fnorm($_[$[+1])); 113*0Sstevel@tonic-gate if ($x eq 'NaN' || $y eq 'NaN') { 114*0Sstevel@tonic-gate 'NaN'; 115*0Sstevel@tonic-gate } else { 116*0Sstevel@tonic-gate local($xm,$xe) = split('E',$x); 117*0Sstevel@tonic-gate local($ym,$ye) = split('E',$y); 118*0Sstevel@tonic-gate ($xm,$xe,$ym,$ye) = ($ym,$ye,$xm,$xe) if ($xe < $ye); 119*0Sstevel@tonic-gate &norm(&'badd($ym,$xm.('0' x ($xe-$ye))),$ye); 120*0Sstevel@tonic-gate } 121*0Sstevel@tonic-gate} 122*0Sstevel@tonic-gate 123*0Sstevel@tonic-gate# subtraction 124*0Sstevel@tonic-gatesub main'fsub { #(fnum_str, fnum_str) return fnum_str 125*0Sstevel@tonic-gate &'fadd($_[$[],&'fneg($_[$[+1])); 126*0Sstevel@tonic-gate} 127*0Sstevel@tonic-gate 128*0Sstevel@tonic-gate# division 129*0Sstevel@tonic-gate# args are dividend, divisor, scale (optional) 130*0Sstevel@tonic-gate# result has at most max(scale, length(dividend), length(divisor)) digits 131*0Sstevel@tonic-gatesub main'fdiv #(fnum_str, fnum_str[,scale]) return fnum_str 132*0Sstevel@tonic-gate{ 133*0Sstevel@tonic-gate local($x,$y,$scale) = (&'fnorm($_[$[]),&'fnorm($_[$[+1]),$_[$[+2]); 134*0Sstevel@tonic-gate if ($x eq 'NaN' || $y eq 'NaN' || $y eq '+0E+0') { 135*0Sstevel@tonic-gate 'NaN'; 136*0Sstevel@tonic-gate } else { 137*0Sstevel@tonic-gate local($xm,$xe) = split('E',$x); 138*0Sstevel@tonic-gate local($ym,$ye) = split('E',$y); 139*0Sstevel@tonic-gate $scale = $div_scale if (!$scale); 140*0Sstevel@tonic-gate $scale = length($xm)-1 if (length($xm)-1 > $scale); 141*0Sstevel@tonic-gate $scale = length($ym)-1 if (length($ym)-1 > $scale); 142*0Sstevel@tonic-gate $scale = $scale + length($ym) - length($xm); 143*0Sstevel@tonic-gate &norm(&round(&'bdiv($xm.('0' x $scale),$ym),&'babs($ym)), 144*0Sstevel@tonic-gate $xe-$ye-$scale); 145*0Sstevel@tonic-gate } 146*0Sstevel@tonic-gate} 147*0Sstevel@tonic-gate 148*0Sstevel@tonic-gate# round int $q based on fraction $r/$base using $rnd_mode 149*0Sstevel@tonic-gatesub round { #(int_str, int_str, int_str) return int_str 150*0Sstevel@tonic-gate local($q,$r,$base) = @_; 151*0Sstevel@tonic-gate if ($q eq 'NaN' || $r eq 'NaN') { 152*0Sstevel@tonic-gate 'NaN'; 153*0Sstevel@tonic-gate } elsif ($rnd_mode eq 'trunc') { 154*0Sstevel@tonic-gate $q; # just truncate 155*0Sstevel@tonic-gate } else { 156*0Sstevel@tonic-gate local($cmp) = &'bcmp(&'bmul($r,'+2'),$base); 157*0Sstevel@tonic-gate if ( $cmp < 0 || 158*0Sstevel@tonic-gate ($cmp == 0 && 159*0Sstevel@tonic-gate ( $rnd_mode eq 'zero' || 160*0Sstevel@tonic-gate ($rnd_mode eq '-inf' && (substr($q,$[,1) eq '+')) || 161*0Sstevel@tonic-gate ($rnd_mode eq '+inf' && (substr($q,$[,1) eq '-')) || 162*0Sstevel@tonic-gate ($rnd_mode eq 'even' && $q =~ /[24680]$/) || 163*0Sstevel@tonic-gate ($rnd_mode eq 'odd' && $q =~ /[13579]$/) )) ) { 164*0Sstevel@tonic-gate $q; # round down 165*0Sstevel@tonic-gate } else { 166*0Sstevel@tonic-gate &'badd($q, ((substr($q,$[,1) eq '-') ? '-1' : '+1')); 167*0Sstevel@tonic-gate # round up 168*0Sstevel@tonic-gate } 169*0Sstevel@tonic-gate } 170*0Sstevel@tonic-gate} 171*0Sstevel@tonic-gate 172*0Sstevel@tonic-gate# round the mantissa of $x to $scale digits 173*0Sstevel@tonic-gatesub main'fround { #(fnum_str, scale) return fnum_str 174*0Sstevel@tonic-gate local($x,$scale) = (&'fnorm($_[$[]),$_[$[+1]); 175*0Sstevel@tonic-gate if ($x eq 'NaN' || $scale <= 0) { 176*0Sstevel@tonic-gate $x; 177*0Sstevel@tonic-gate } else { 178*0Sstevel@tonic-gate local($xm,$xe) = split('E',$x); 179*0Sstevel@tonic-gate if (length($xm)-1 <= $scale) { 180*0Sstevel@tonic-gate $x; 181*0Sstevel@tonic-gate } else { 182*0Sstevel@tonic-gate &norm(&round(substr($xm,$[,$scale+1), 183*0Sstevel@tonic-gate "+0".substr($xm,$[+$scale+1,1),"+10"), 184*0Sstevel@tonic-gate $xe+length($xm)-$scale-1); 185*0Sstevel@tonic-gate } 186*0Sstevel@tonic-gate } 187*0Sstevel@tonic-gate} 188*0Sstevel@tonic-gate 189*0Sstevel@tonic-gate# round $x at the 10 to the $scale digit place 190*0Sstevel@tonic-gatesub main'ffround { #(fnum_str, scale) return fnum_str 191*0Sstevel@tonic-gate local($x,$scale) = (&'fnorm($_[$[]),$_[$[+1]); 192*0Sstevel@tonic-gate if ($x eq 'NaN') { 193*0Sstevel@tonic-gate 'NaN'; 194*0Sstevel@tonic-gate } else { 195*0Sstevel@tonic-gate local($xm,$xe) = split('E',$x); 196*0Sstevel@tonic-gate if ($xe >= $scale) { 197*0Sstevel@tonic-gate $x; 198*0Sstevel@tonic-gate } else { 199*0Sstevel@tonic-gate $xe = length($xm)+$xe-$scale; 200*0Sstevel@tonic-gate if ($xe < 1) { 201*0Sstevel@tonic-gate '+0E+0'; 202*0Sstevel@tonic-gate } elsif ($xe == 1) { 203*0Sstevel@tonic-gate # The first substr preserves the sign, which means that 204*0Sstevel@tonic-gate # we'll pass a non-normalized "-0" to &round when rounding 205*0Sstevel@tonic-gate # -0.006 (for example), purely so that &round won't lose 206*0Sstevel@tonic-gate # the sign. 207*0Sstevel@tonic-gate &norm(&round(substr($xm,$[,1).'0', 208*0Sstevel@tonic-gate "+0".substr($xm,$[+1,1),"+10"), $scale); 209*0Sstevel@tonic-gate } else { 210*0Sstevel@tonic-gate &norm(&round(substr($xm,$[,$xe), 211*0Sstevel@tonic-gate "+0".substr($xm,$[+$xe,1),"+10"), $scale); 212*0Sstevel@tonic-gate } 213*0Sstevel@tonic-gate } 214*0Sstevel@tonic-gate } 215*0Sstevel@tonic-gate} 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gate# compare 2 values returns one of undef, <0, =0, >0 218*0Sstevel@tonic-gate# returns undef if either or both input value are not numbers 219*0Sstevel@tonic-gatesub main'fcmp #(fnum_str, fnum_str) return cond_code 220*0Sstevel@tonic-gate{ 221*0Sstevel@tonic-gate local($x, $y) = (&'fnorm($_[$[]),&'fnorm($_[$[+1])); 222*0Sstevel@tonic-gate if ($x eq "NaN" || $y eq "NaN") { 223*0Sstevel@tonic-gate undef; 224*0Sstevel@tonic-gate } else { 225*0Sstevel@tonic-gate ord($y) <=> ord($x) 226*0Sstevel@tonic-gate || 227*0Sstevel@tonic-gate ( local($xm,$xe,$ym,$ye) = split('E', $x."E$y"), 228*0Sstevel@tonic-gate (($xe <=> $ye) * (substr($x,$[,1).'1') 229*0Sstevel@tonic-gate || &bigint'cmp($xm,$ym)) 230*0Sstevel@tonic-gate ); 231*0Sstevel@tonic-gate } 232*0Sstevel@tonic-gate} 233*0Sstevel@tonic-gate 234*0Sstevel@tonic-gate# square root by Newtons method. 235*0Sstevel@tonic-gatesub main'fsqrt { #(fnum_str[, scale]) return fnum_str 236*0Sstevel@tonic-gate local($x, $scale) = (&'fnorm($_[$[]), $_[$[+1]); 237*0Sstevel@tonic-gate if ($x eq 'NaN' || $x =~ /^-/) { 238*0Sstevel@tonic-gate 'NaN'; 239*0Sstevel@tonic-gate } elsif ($x eq '+0E+0') { 240*0Sstevel@tonic-gate '+0E+0'; 241*0Sstevel@tonic-gate } else { 242*0Sstevel@tonic-gate local($xm, $xe) = split('E',$x); 243*0Sstevel@tonic-gate $scale = $div_scale if (!$scale); 244*0Sstevel@tonic-gate $scale = length($xm)-1 if ($scale < length($xm)-1); 245*0Sstevel@tonic-gate local($gs, $guess) = (1, sprintf("1E%+d", (length($xm)+$xe-1)/2)); 246*0Sstevel@tonic-gate while ($gs < 2*$scale) { 247*0Sstevel@tonic-gate $guess = &'fmul(&'fadd($guess,&'fdiv($x,$guess,$gs*2)),".5"); 248*0Sstevel@tonic-gate $gs *= 2; 249*0Sstevel@tonic-gate } 250*0Sstevel@tonic-gate &'fround($guess, $scale); 251*0Sstevel@tonic-gate } 252*0Sstevel@tonic-gate} 253*0Sstevel@tonic-gate 254*0Sstevel@tonic-gate1; 255