The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Math::BigApprox - Fast and small way to closely approximate very large values.

SYNOPSIS

    require Math::BigApprox;

    my $cards=  Math::BigApprox->new( 52 );
    my $decks=  ! $cards;                   # Factorial
    my $deals=  $cards-4 ^ $cards;          # Product of sequence
    my $hands=  $deals / ! Math::BigApprox->new( 5 );

    # also

    use Math::BigApprox qw( c Prod Fact );

    $hands= ( 52-4 ^ c(52) ) / !c(5);
    # or
    $hands= Prod( 52-4, 52 ) / Fact(5)

DESCRIPTION

Math::BigApprox stores numbers as the logarithm of their (absolute) value (along with separately tracking their sign) and so can keep track of numbers having absolute value less than about 1e100000000 with several digits of precision (and quite a bit of precision for numbers that are not quite so ridiculously large). You also lose precision for numbers ridiculuous close to zero like 1e-10000000000.

Therefore, Math::BigApprox numbers never require a large amount of memory and calculations with them are rather fast (usually about as fast as you'll get with overloaded operations in Perl).

It can even do calculations with numbers as large as 10**(1e300) (which is too large for this space even when written as "1e..."). But for such obscenely huge numbers it only knows their magnitude, having zero digits of precision in the mantissa (or "significand") but having reasonable precision in the base-10 exponent (the number after the "e"). It even displays such numbers as something like "1e1.0217e300" since the usual XeN scientific notation is too cumbersome for such obscenely large numbers.

Math::BigApprox overloads the basic arithmatic operations (plus a few others) so that, once you've created a Math::BigApprox number object, you can just use it like an ordinary Perl variable to make calculations, including using ordinary numbers in those calculations. Calculations that mix Math::BigApprox numbers with other types of overloaded numbers are not supported at this time.

Addition and subtraction are done carefully so that combining values of wildly different magnitude results in an expression like exp(-1e100) (which returns 0, a result that can safely be added/subtracted, giving a reasonably accurate final result) not like exp(1e100) (which returns "infinity", fouling any further calculations).

Methods

new

    my $six= Math::Approx->new( 6 );
    my $halfdozen= $six->new();     # Makes a copy
    my $ten= $six->new( 10 );       # Convenient short-hand

Get

In a scalar context, $x->Get() returns "0" if $x==0, otherwise log(abs($x)). The return value is a simple Perl (floating-point) number, not a Math::BigApprox object.

In a list context, $x->Get() returns two scalars: the above value followed by $x->Sign().

Sign

$x->Sign() returns "-1" if $x < 0, "0" if $x == 0, and "1" if 0 < $x.

$x->Sign($y) sets the sign of $x to match the sign of $y. $y doesn't have to be a plain Perl number or a Math::BigApprox number object; it can be anything that can be used in 0<$y and 0==$y. The previous value of the sign is returned.

Operations

+, -, *, /, **

The basic arithmatic operations (including unary "-", negation) serve the traditional purposes, returning a new, overloaded Math::BigApprox object.

Of course, $x**0 is "1" (even for $x==0) while 0**$x is "0" (unless $x==0).

Note that overload.pm handles expressions like 10**$x where $x is a Math::BigApprox number object or even 1+$x, so you don't have to convert all parts of an expression into Math::BigApprox objects, nor even the first term in the expression.

<<, >>

$x << $y calculates $x * 2**$y.

$x >> $y calculates $x / 2**$y.

Each returns a Math::BigApprox number object. Note that $y doesn't have to be an integer (nor positive).

!

Logical negation has been usurped to perform factorial. It being a unary operation even means its precedence is reasonable for such a use, binding tighter than the arithmatic operations so !$x/!$y works as expected.

^

Bit-wise xor has been usurped to perform the product of a series. 1^$x is the same as !$x, that is, 1*2*3*...*$x. $x^$y is $x*($x+1)*($x+2)*...*$y (unless $y-$x is not an integer, in which case the multiplications stop at the largest integer $N where $x+$N <= $y). If $y < $x then $x^$y is "1" (the product of nothing).

Note that ^ binds very losely so $x+1 ^ 2*$y works as expected but $x^$y / !$z really means $x ^ ($y/!$z) and so needs to be written as ($x^$y) / !$z.

<=>

All of the numerical comparison operators work in the traditional manner, comparing the values. Only <=> is specifically overloaded but overload.pm automatically generates the other numeric comparisons from it.

Note that two values are considered numerically equal (==) if they have the same string representation (like eq). This avoids some of the pitfalls of using == on floating-point values. So == really means "approximately numerically equal".

abs, log, sqrt

These math functions work in the traditional manner. log($x) returns an ordinary number, not a Math::BigApprox number, and dies if $x is not positive [see also Get()]. abs($x) and sqrt($x) return Math::BigApprox number objects (when $x is a Math::BigApprox object).

""

Math::BigApprox values are displayed (stringified) mostly like oridinary Perl numbers, using simple decimal notation for mundane values ("-45.3") and scientific notation for huge and tiny values ("-2.687e-97"). For obscenely huge/tiny values, a "double scientific notation" format is used ("1e2.68e213").

Note that Math::BigApprox doesn't overload string concatenation (.), so ''.$x stringifies $x just like "$x".

0+

Using a Math:BigApprox number object in a way that requires a simple numeric value attempts to compute a simple number. If the number is too tiny, then 0 is used. If the number is too huge, then an "infinity" reserved value will be used (the details of this vary between platforms).

For example

    printf "%g", 100 ** Math::BigApprox->new(500);

might produce

    1.#INF

Note that, despite the name used by overload.pm, 0+$x doesn't return a simple number; it returns a Math::BigApprox number object (when $x is such), a copy of $x.

Boolean

Using a Math::BigApprox alone as a Boolean expression is a fatal error. This is to help prevent accidental attempts to use ! as "Boolean not".

    if(  $x  ) {        # A fatal error
    if(  0 != $x  ) {   # What to use instead of the above

    if(  ! $x  ) {      # A coding error
    if(  0 != !$x  ) {  # What the above really means
    if(  0 == $x  ) {   # What to use instead

Other

Any other operations such as other Boolean or bit-wise operations are not supported and using them may cause strange things to happen. Some of these may be usurped for other purposes in a future release of this module.

Several math functions are not explicitly supported (atan2, cos, sin, exp, and int). Using them on a Math::BigApprox number will cause a simple number to be computed (possibly giving "infinity") and that value to be passed to the math function. This situation is unlikely to change (these functions rarely make sense to use on really huge numbers).

Exportable Functions

The following fuctions can optionally be exported.

c

c($num) is just short-hand for Math::BigApprox->new($num). "c" is a standard abbreviate for "circa" which means "approximate".

Fact

Fact($n) returns the factorial of $n. Here $n is a regular Perl number rather than a Math::BigApprox number object, which allows the computation to be done slightly more efficiently than !c($n).

Prod

Prod($from,$to) returns the product of the numbers in the range $from..$to. $from and $to are a regular Perl numbers rather than Math::BigApprox number objects, which allows the computation to be done slightly more efficiently than c($from) ^ c($to).

FUTURE IDEAS

Add an $x->e($y) method that multiples $x by 10**$y and a c'tor that takes a separate significand and base-10 exponent.

SHORTCOMINGS

new() should accept string values of the same format that Math::BigApprox values are displayed in. Currently new() only accepts ordinary Perl floating-point numbers (or things that can be converted to such).

Floating point imprecision means that $x^$y or even !$x might not perform the final multiplication. Since == really means eq for Math::BigApprox numbers, the dynamic display precision should prevent this from being a problem in most cases.

Calculations that mix Math::BigApprox numbers with other types of overloaded numbers are not supported at this time.

Writing a class that inherits from Math::BigApprox is not supported at this time.

Requesting that bare numeric constants be silently promoted to Math::BigApprox numbers is not currently supported.

Calculating 1e-1001 ** 1e1002 (for example) produces 1e-infinity which is displayed as "0" (and so also == 0) but should be converted to a real zero value so that it behaves like "0" in all situations. Then again, 1/$notzero produces infinity, which may be even more appropriate. This also provides a way to represent "negative zero".

Raising a negative number to a power often ignores that the number is negative. c(-1)**.5 is the same as c(1)**.5 (not "NaN" because it should be imaginary). c(-1)**$big will usually be just 1, even when you might consider $big to be odd (we make a half-hearted effort to decide whether $big is odd, but don't depend on it nor be surprised if the details of this "effort" change in future releases).

A few places die that should croak.

AUTHOR

Tye McQueen, http://perlmonks.org/?node=tye

SEE ALSO

The Museum of Jurassic Technology, L.A., CA, USA.

and http://perlmonks.org/?node_id=631244