SYN SYN
[p5sagit/p5-mst-13.2.git] / lib / Math / BigFloat.pm
index 576f341..74a023e 100644 (file)
@@ -4,15 +4,14 @@ use Math::BigInt;
 
 use Exporter;  # just for use to be happy
 @ISA = (Exporter);
+$VERSION = '0.01';     # never had version before
 
 use overload
 '+'    =>      sub {new Math::BigFloat &fadd},
 '-'    =>      sub {new Math::BigFloat
                       $_[2]? fsub($_[1],${$_[0]}) : fsub(${$_[0]},$_[1])},
-'<=>'  =>      sub {new Math::BigFloat
-                      $_[2]? fcmp($_[1],${$_[0]}) : fcmp(${$_[0]},$_[1])},
-'cmp'  =>      sub {new Math::BigFloat
-                      $_[2]? ($_[1] cmp ${$_[0]}) : (${$_[0]} cmp $_[1])},
+'<=>'  =>      sub {$_[2]? fcmp($_[1],${$_[0]}) : fcmp(${$_[0]},$_[1])},
+'cmp'  =>      sub {$_[2]? ($_[1] cmp ${$_[0]}) : (${$_[0]} cmp $_[1])},
 '*'    =>      sub {new Math::BigFloat &fmul},
 '/'    =>      sub {new Math::BigFloat 
                       $_[2]? scalar fdiv($_[1],${$_[0]}) :
@@ -28,9 +27,9 @@ qw(
 sub new {
   my ($class) = shift;
   my ($foo) = fnorm(shift);
-  panic("Not a number initialized to Math::BigFloat") if $foo eq "NaN";
   bless \$foo, $class;
 }
+
 sub numify { 0 + "${$_[0]}" }  # Not needed, additional overhead
                                # comparing to direct compilation based on
                                # stringify
@@ -76,6 +75,7 @@ sub fnorm; sub fsqrt;
 sub fnorm { #(string) return fnum_str
     local($_) = @_;
     s/\s+//g;                               # strip white space
+    no warnings;       # $4 and $5 below might legitimately be undefined
     if (/^([+-]?)(\d*)(\.(\d*))?([Ee]([+-]?\d+))?$/ && "$2$4" ne '') {
        &norm(($1 ? "$1$2$4" : "+$2$4"),(($4 ne '') ? $6-length($4) : $6));
     } else {
@@ -86,6 +86,7 @@ sub fnorm { #(string) return fnum_str
 # normalize number -- for internal use
 sub norm { #(mantissa, exponent) return fnum_str
     local($_, $exp) = @_;
+       $exp = 0 unless defined $exp;
     if ($_ eq 'NaN') {
        'NaN';
     } else {
@@ -159,7 +160,8 @@ sub fdiv #(fnum_str, fnum_str[,scale]) return fnum_str
        $scale = length($xm)-1 if (length($xm)-1 > $scale);
        $scale = length($ym)-1 if (length($ym)-1 > $scale);
        $scale = $scale + length($ym) - length($xm);
-       &norm(&round(Math::BigInt::bdiv($xm.('0' x $scale),$ym),$ym),
+       &norm(&round(Math::BigInt::bdiv($xm.('0' x $scale),$ym),
+                   Math::BigInt::babs($ym)),
            $xe-$ye-$scale);
     }
 }
@@ -174,12 +176,14 @@ sub round { #(int_str, int_str, int_str) return int_str
     } else {
        local($cmp) = Math::BigInt::bcmp(Math::BigInt::bmul($r,'+2'),$base);
        if ( $cmp < 0 ||
-                ($cmp == 0 &&
-                 ( $rnd_mode eq 'zero'                             ||
+                ($cmp == 0 &&                                          (
+                  ($rnd_mode eq 'zero'                            ) ||
                   ($rnd_mode eq '-inf' && (substr($q,$[,1) eq '+')) ||
                   ($rnd_mode eq '+inf' && (substr($q,$[,1) eq '-')) ||
-                  ($rnd_mode eq 'even' && $q =~ /[24680]$/)        ||
-                  ($rnd_mode eq 'odd'  && $q =~ /[13579]$/)        )) ) {
+                  ($rnd_mode eq 'even' && $q =~ /[13579]$/        ) ||
+                  ($rnd_mode eq 'odd'  && $q =~ /[24680]$/        )    )
+                 ) 
+               ) {
            $q;                     # round down
        } else {
            Math::BigInt::badd($q, ((substr($q,$[,1) eq '-') ? '-1' : '+1'));
@@ -219,7 +223,11 @@ sub ffround { #(fnum_str, scale) return fnum_str
            if ($xe < 1) {
                '+0E+0';
            } elsif ($xe == 1) {
-               &norm(&round('+0',"+0".substr($xm,$[+1,1),"+10"), $scale);
+               # The first substr preserves the sign, passing a non-
+               # normalized "-0" to &round when rounding -0.006 (for
+               # example), purely so &round won't lose the sign.
+               &norm(&round(substr($xm,$[,1).'0',
+                     "+0".substr($xm,$[+1,1),"+10"), $scale);
            } else {
                &norm(&round(substr($xm,$[,$xe),
                      "+0".substr($xm,$[+$xe,1),"+10"), $scale);
@@ -236,12 +244,21 @@ sub fcmp #(fnum_str, fnum_str) return cond_code
     if ($x eq "NaN" || $y eq "NaN") {
        undef;
     } else {
-       ord($y) <=> ord($x)
-       ||
-       (  local($xm,$xe,$ym,$ye) = split('E', $x."E$y"),
-            (($xe <=> $ye) * (substr($x,$[,1).'1')
-             || Math::BigInt::cmp($xm,$ym))
-       );
+       local($xm,$xe,$ym,$ye) = split('E', $x."E$y");
+       if ($xm eq '+0' || $ym eq '+0') {
+           return $xm <=> $ym;
+       }
+       if ( $xe < $ye )        # adjust the exponents to be equal
+       {
+               $ym .= '0' x ($ye - $xe);
+               $ye = $xe;
+       }
+       elsif ( $ye < $xe )     # same here
+       {
+               $xm .= '0' x ($xe - $ye);
+               $xe = $ye;
+       }
+       return Math::BigInt::cmp($xm,$ym);
     }
 }
 
@@ -301,7 +318,7 @@ floats as
 =item number format
 
 canonical strings have the form /[+-]\d+E[+-]\d+/ .  Input values can
-have inbedded whitespace.
+have embedded whitespace.
 
 =item Error returns 'NaN'
 
@@ -310,9 +327,24 @@ negative number.
 
 =item Division is computed to 
 
-C<max($div_scale,length(dividend)+length(divisor))> digits by default.
+C<max($Math::BigFloat::div_scale,length(dividend)+length(divisor))>
+digits by default.
 Also used for default sqrt scale.
 
+=item Rounding is performed
+
+according to the value of
+C<$Math::BigFloat::rnd_mode>:
+
+  trunc     truncate the value
+  zero      round towards 0
+  +inf      round towards +infinity (round up)
+  -inf      round towards -infinity (round down)
+  even      round to the nearest, .5 to the even digit
+  odd       round to the nearest, .5 to the odd digit
+
+The default is C<even> rounding.
+
 =back
 
 =head1 BUGS
@@ -320,6 +352,15 @@ Also used for default sqrt scale.
 The current version of this module is a preliminary version of the
 real thing that is currently (as of perl5.002) under development.
 
+The printf subroutine does not use the value of
+C<$Math::BigFloat::rnd_mode> when rounding values for printing.
+Consequently, the way to print rounded values is
+to specify the number of digits both as an
+argument to C<ffround> and in the C<%f> printf string,
+as follows:
+
+  printf "%.3f\n", $bigfloat->ffround(-3);
+
 =head1 AUTHOR
 
 Mark Biggar