Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

398 lines
11 KiB

  1. package Math::BigFloat;
  2. use Math::BigInt;
  3. use Exporter; # just for use to be happy
  4. @ISA = (Exporter);
  5. $VERSION = '0.02';
  6. use overload
  7. '+' => sub {new Math::BigFloat &fadd},
  8. '-' => sub {new Math::BigFloat
  9. $_[2]? fsub($_[1],${$_[0]}) : fsub(${$_[0]},$_[1])},
  10. '<=>' => sub {$_[2]? fcmp($_[1],${$_[0]}) : fcmp(${$_[0]},$_[1])},
  11. 'cmp' => sub {$_[2]? ($_[1] cmp ${$_[0]}) : (${$_[0]} cmp $_[1])},
  12. '*' => sub {new Math::BigFloat &fmul},
  13. '/' => sub {new Math::BigFloat
  14. $_[2]? scalar fdiv($_[1],${$_[0]}) :
  15. scalar fdiv(${$_[0]},$_[1])},
  16. '%' => sub {new Math::BigFloat
  17. $_[2]? scalar fmod($_[1],${$_[0]}) :
  18. scalar fmod(${$_[0]},$_[1])},
  19. 'neg' => sub {new Math::BigFloat &fneg},
  20. 'abs' => sub {new Math::BigFloat &fabs},
  21. qw(
  22. "" stringify
  23. 0+ numify) # Order of arguments unsignificant
  24. ;
  25. sub new {
  26. my ($class) = shift;
  27. my ($foo) = fnorm(shift);
  28. bless \$foo, $class;
  29. }
  30. sub numify { 0 + "${$_[0]}" } # Not needed, additional overhead
  31. # comparing to direct compilation based on
  32. # stringify
  33. sub stringify {
  34. my $n = ${$_[0]};
  35. my $minus = ($n =~ s/^([+-])// && $1 eq '-');
  36. $n =~ s/E//;
  37. $n =~ s/([-+]\d+)$//;
  38. my $e = $1;
  39. my $ln = length($n);
  40. if ( defined $e )
  41. {
  42. if ($e > 0) {
  43. $n .= "0" x $e . '.';
  44. } elsif (abs($e) < $ln) {
  45. substr($n, $ln + $e, 0) = '.';
  46. } else {
  47. $n = '.' . ("0" x (abs($e) - $ln)) . $n;
  48. }
  49. }
  50. $n = "-$n" if $minus;
  51. # 1 while $n =~ s/(.*\d)(\d\d\d)/$1,$2/;
  52. return $n;
  53. }
  54. $div_scale = 40;
  55. # Rounding modes one of 'even', 'odd', '+inf', '-inf', 'zero' or 'trunc'.
  56. $rnd_mode = 'even';
  57. sub fadd; sub fsub; sub fmul; sub fdiv;
  58. sub fneg; sub fabs; sub fcmp;
  59. sub fround; sub ffround;
  60. sub fnorm; sub fsqrt;
  61. # Convert a number to canonical string form.
  62. # Takes something that looks like a number and converts it to
  63. # the form /^[+-]\d+E[+-]\d+$/.
  64. sub fnorm { #(string) return fnum_str
  65. local($_) = @_;
  66. s/\s+//g; # strip white space
  67. no warnings; # $4 and $5 below might legitimately be undefined
  68. if (/^([+-]?)(\d*)(\.(\d*))?([Ee]([+-]?\d+))?$/ && "$2$4" ne '') {
  69. &norm(($1 ? "$1$2$4" : "+$2$4"),(($4 ne '') ? $6-length($4) : $6));
  70. } else {
  71. 'NaN';
  72. }
  73. }
  74. # normalize number -- for internal use
  75. sub norm { #(mantissa, exponent) return fnum_str
  76. local($_, $exp) = @_;
  77. $exp = 0 unless defined $exp;
  78. if ($_ eq 'NaN') {
  79. 'NaN';
  80. } else {
  81. s/^([+-])0+/$1/; # strip leading zeros
  82. if (length($_) == 1) {
  83. '+0E+0';
  84. } else {
  85. $exp += length($1) if (s/(0+)$//); # strip trailing zeros
  86. sprintf("%sE%+ld", $_, $exp);
  87. }
  88. }
  89. }
  90. # negation
  91. sub fneg { #(fnum_str) return fnum_str
  92. local($_) = fnorm($_[$[]);
  93. vec($_,0,8) ^= ord('+') ^ ord('-') unless $_ eq '+0E+0'; # flip sign
  94. s/^H/N/;
  95. $_;
  96. }
  97. # absolute value
  98. sub fabs { #(fnum_str) return fnum_str
  99. local($_) = fnorm($_[$[]);
  100. s/^-/+/; # mash sign
  101. $_;
  102. }
  103. # multiplication
  104. sub fmul { #(fnum_str, fnum_str) return fnum_str
  105. local($x,$y) = (fnorm($_[$[]),fnorm($_[$[+1]));
  106. if ($x eq 'NaN' || $y eq 'NaN') {
  107. 'NaN';
  108. } else {
  109. local($xm,$xe) = split('E',$x);
  110. local($ym,$ye) = split('E',$y);
  111. &norm(Math::BigInt::bmul($xm,$ym),$xe+$ye);
  112. }
  113. }
  114. # addition
  115. sub fadd { #(fnum_str, fnum_str) return fnum_str
  116. local($x,$y) = (fnorm($_[$[]),fnorm($_[$[+1]));
  117. if ($x eq 'NaN' || $y eq 'NaN') {
  118. 'NaN';
  119. } else {
  120. local($xm,$xe) = split('E',$x);
  121. local($ym,$ye) = split('E',$y);
  122. ($xm,$xe,$ym,$ye) = ($ym,$ye,$xm,$xe) if ($xe < $ye);
  123. &norm(Math::BigInt::badd($ym,$xm.('0' x ($xe-$ye))),$ye);
  124. }
  125. }
  126. # subtraction
  127. sub fsub { #(fnum_str, fnum_str) return fnum_str
  128. fadd($_[$[],fneg($_[$[+1]));
  129. }
  130. # division
  131. # args are dividend, divisor, scale (optional)
  132. # result has at most max(scale, length(dividend), length(divisor)) digits
  133. sub fdiv #(fnum_str, fnum_str[,scale]) return fnum_str
  134. {
  135. local($x,$y,$scale) = (fnorm($_[$[]),fnorm($_[$[+1]),$_[$[+2]);
  136. if ($x eq 'NaN' || $y eq 'NaN' || $y eq '+0E+0') {
  137. 'NaN';
  138. } else {
  139. local($xm,$xe) = split('E',$x);
  140. local($ym,$ye) = split('E',$y);
  141. $scale = $div_scale if (!$scale);
  142. $scale = length($xm)-1 if (length($xm)-1 > $scale);
  143. $scale = length($ym)-1 if (length($ym)-1 > $scale);
  144. $scale = $scale + length($ym) - length($xm);
  145. &norm(&round(Math::BigInt::bdiv($xm.('0' x $scale),$ym),
  146. Math::BigInt::babs($ym)),
  147. $xe-$ye-$scale);
  148. }
  149. }
  150. # modular division
  151. # args are dividend, divisor
  152. sub fmod #(fnum_str, fnum_str) return fnum_str
  153. {
  154. local($x,$y) = (fnorm($_[$[]),fnorm($_[$[+1]));
  155. if ($x eq 'NaN' || $y eq 'NaN' || $y eq '+0E+0') {
  156. 'NaN';
  157. } else {
  158. local($xm,$xe) = split('E',$x);
  159. local($ym,$ye) = split('E',$y);
  160. if ( $xe < $ye )
  161. {
  162. $ym .= ('0' x ($ye-$xe));
  163. }
  164. else
  165. {
  166. $xm .= ('0' x ($xe-$ye));
  167. }
  168. &norm(Math::BigInt::bmod($xm,$ym));
  169. }
  170. }
  171. # round int $q based on fraction $r/$base using $rnd_mode
  172. sub round { #(int_str, int_str, int_str) return int_str
  173. local($q,$r,$base) = @_;
  174. if ($q eq 'NaN' || $r eq 'NaN') {
  175. 'NaN';
  176. } elsif ($rnd_mode eq 'trunc') {
  177. $q; # just truncate
  178. } else {
  179. local($cmp) = Math::BigInt::bcmp(Math::BigInt::bmul($r,'+2'),$base);
  180. if ( $cmp < 0 ||
  181. ($cmp == 0 && (
  182. ($rnd_mode eq 'zero' ) ||
  183. ($rnd_mode eq '-inf' && (substr($q,$[,1) eq '+')) ||
  184. ($rnd_mode eq '+inf' && (substr($q,$[,1) eq '-')) ||
  185. ($rnd_mode eq 'even' && $q =~ /[24680]$/ ) ||
  186. ($rnd_mode eq 'odd' && $q =~ /[13579]$/ ) )
  187. )
  188. ) {
  189. $q; # round down
  190. } else {
  191. Math::BigInt::badd($q, ((substr($q,$[,1) eq '-') ? '-1' : '+1'));
  192. # round up
  193. }
  194. }
  195. }
  196. # round the mantissa of $x to $scale digits
  197. sub fround { #(fnum_str, scale) return fnum_str
  198. local($x,$scale) = (fnorm($_[$[]),$_[$[+1]);
  199. if ($x eq 'NaN' || $scale <= 0) {
  200. $x;
  201. } else {
  202. local($xm,$xe) = split('E',$x);
  203. if (length($xm)-1 <= $scale) {
  204. $x;
  205. } else {
  206. &norm(&round(substr($xm,$[,$scale+1),
  207. "+0".substr($xm,$[+$scale+1),"+1"."0" x length(substr($xm,$[+$scale+1))),
  208. $xe+length($xm)-$scale-1);
  209. }
  210. }
  211. }
  212. # round $x at the 10 to the $scale digit place
  213. sub ffround { #(fnum_str, scale) return fnum_str
  214. local($x,$scale) = (fnorm($_[$[]),$_[$[+1]);
  215. if ($x eq 'NaN') {
  216. 'NaN';
  217. } else {
  218. local($xm,$xe) = split('E',$x);
  219. if ($xe >= $scale) {
  220. $x;
  221. } else {
  222. $xe = length($xm)+$xe-$scale;
  223. if ($xe < 1) {
  224. '+0E+0';
  225. } elsif ($xe == 1) {
  226. # The first substr preserves the sign, passing a non-
  227. # normalized "-0" to &round when rounding -0.006 (for
  228. # example), purely so &round won't lose the sign.
  229. &norm(&round(substr($xm,$[,1).'0',
  230. "+0".substr($xm,$[+1),
  231. "+1"."0" x length(substr($xm,$[+1))), $scale);
  232. } else {
  233. &norm(&round(substr($xm,$[,$xe),
  234. "+0".substr($xm,$[+$xe),
  235. "+1"."0" x length(substr($xm,$[+$xe))), $scale);
  236. }
  237. }
  238. }
  239. }
  240. # compare 2 values returns one of undef, <0, =0, >0
  241. # returns undef if either or both input value are not numbers
  242. sub fcmp #(fnum_str, fnum_str) return cond_code
  243. {
  244. local($x, $y) = (fnorm($_[$[]),fnorm($_[$[+1]));
  245. if ($x eq "NaN" || $y eq "NaN") {
  246. undef;
  247. } else {
  248. local($xm,$xe,$ym,$ye) = split('E', $x."E$y");
  249. if ($xm eq '+0' || $ym eq '+0') {
  250. return $xm <=> $ym;
  251. }
  252. if ( $xe < $ye ) # adjust the exponents to be equal
  253. {
  254. $ym .= '0' x ($ye - $xe);
  255. $ye = $xe;
  256. }
  257. elsif ( $ye < $xe ) # same here
  258. {
  259. $xm .= '0' x ($xe - $ye);
  260. $xe = $ye;
  261. }
  262. return Math::BigInt::cmp($xm,$ym);
  263. }
  264. }
  265. # square root by Newtons method.
  266. sub fsqrt { #(fnum_str[, scale]) return fnum_str
  267. local($x, $scale) = (fnorm($_[$[]), $_[$[+1]);
  268. if ($x eq 'NaN' || $x =~ /^-/) {
  269. 'NaN';
  270. } elsif ($x eq '+0E+0') {
  271. '+0E+0';
  272. } else {
  273. local($xm, $xe) = split('E',$x);
  274. $scale = $div_scale if (!$scale);
  275. $scale = length($xm)-1 if ($scale < length($xm)-1);
  276. local($gs, $guess) = (1, sprintf("1E%+d", (length($xm)+$xe-1)/2));
  277. while ($gs < 2*$scale) {
  278. $guess = fmul(fadd($guess,fdiv($x,$guess,$gs*2)),".5");
  279. $gs *= 2;
  280. }
  281. new Math::BigFloat &fround($guess, $scale);
  282. }
  283. }
  284. 1;
  285. __END__
  286. =head1 NAME
  287. Math::BigFloat - Arbitrary length float math package
  288. =head1 SYNOPSIS
  289. use Math::BigFloat;
  290. $f = Math::BigFloat->new($string);
  291. $f->fadd(NSTR) return NSTR addition
  292. $f->fsub(NSTR) return NSTR subtraction
  293. $f->fmul(NSTR) return NSTR multiplication
  294. $f->fdiv(NSTR[,SCALE]) returns NSTR division to SCALE places
  295. $f->fmod(NSTR) returns NSTR modular remainder
  296. $f->fneg() return NSTR negation
  297. $f->fabs() return NSTR absolute value
  298. $f->fcmp(NSTR) return CODE compare undef,<0,=0,>0
  299. $f->fround(SCALE) return NSTR round to SCALE digits
  300. $f->ffround(SCALE) return NSTR round at SCALEth place
  301. $f->fnorm() return (NSTR) normalize
  302. $f->fsqrt([SCALE]) return NSTR sqrt to SCALE places
  303. =head1 DESCRIPTION
  304. All basic math operations are overloaded if you declare your big
  305. floats as
  306. $float = new Math::BigFloat "2.123123123123123123123123123123123";
  307. =over 2
  308. =item number format
  309. canonical strings have the form /[+-]\d+E[+-]\d+/ . Input values can
  310. have embedded whitespace.
  311. =item Error returns 'NaN'
  312. An input parameter was "Not a Number" or divide by zero or sqrt of
  313. negative number.
  314. =item Division is computed to
  315. C<max($Math::BigFloat::div_scale,length(dividend)+length(divisor))>
  316. digits by default.
  317. Also used for default sqrt scale.
  318. =item Rounding is performed
  319. according to the value of
  320. C<$Math::BigFloat::rnd_mode>:
  321. trunc truncate the value
  322. zero round towards 0
  323. +inf round towards +infinity (round up)
  324. -inf round towards -infinity (round down)
  325. even round to the nearest, .5 to the even digit
  326. odd round to the nearest, .5 to the odd digit
  327. The default is C<even> rounding.
  328. =back
  329. =head1 BUGS
  330. The current version of this module is a preliminary version of the
  331. real thing that is currently (as of perl5.002) under development.
  332. The printf subroutine does not use the value of
  333. C<$Math::BigFloat::rnd_mode> when rounding values for printing.
  334. Consequently, the way to print rounded values is
  335. to specify the number of digits both as an
  336. argument to C<ffround> and in the C<%f> printf string,
  337. as follows:
  338. printf "%.3f\n", $bigfloat->ffround(-3);
  339. =head1 AUTHOR
  340. Mark Biggar
  341. Patches by John Peacock Apr 2001
  342. =cut