Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Efficiently working with huge exponents

by wanna_code_perl (Friar)
on Mar 20, 2015 at 11:30 UTC ( [id://1120730]=perlquestion: print w/replies, xml ) Need Help??

wanna_code_perl has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks!

I need to work with some rather large exponents (much too big for ordinary Perl scalar), on the order of 2**1024000.

Here's my test program:

#!/usr/bin/env perl use 5.014; use Math::BigFloat; my $POW = 1024000; my $n = Math::BigFloat->new(2); say "BigFloat result: " . $n->pow($POW);

On my system, this takes over 3 minutes to run, and works with perfect precision (i.e., I get a 308255-digit integer). I don't need perfect precision, so I set $n->accuracy(10);, which is plenty for my purposes. However, I still get a huge integer as a result (only this time, it's mostly zeroes), and it still takes over 3 minutes to run.

What I want is a limited-precision floating point number such as 5.194693e308255, that I can compare to other such numbers for sorting, display, and the like. Any more than, say, 10 significant figures, is a waste.

Is there a way (with or without BigFloat) to (quickly) calculate a limited-precision float like 5.195e308255? I feel like I'm missing something obvious.

Replies are listed 'Best First'.
Re: Efficiently working with huge exponents
by syphilis (Archbishop) on Mar 20, 2015 at 13:34 UTC
    I'd go straight to Math::MPFR (which uses the mpfr library, which in turn uses the gmp library).
    However, in doing so I'm plugging one of my own modules - which perhaps lessens the value of my advice.
    Anyway, on my Windows box:
    C:\_32>perl -MMath::MPFR=":mpfr" -le "Rmpfr_set_default_prec(1024001); +$x = Math::MPFR->new(2); $x **= 1024000; print $x;" >out.txt
    The output file reveals:
    5.194693363199925.....490149376e308254
    The result is instant (maybe a quarter of a second).
    The length of the string that is output correlates nicely to yours. It is 308263, which is 308255 + 1 (for the decimal point) + 7 (for the "e308254").
    Do we also agree on the value ?

    Maximum precision for my mpfr library (and hence for my Math::MPFR) is 1073741823 bits.

    Update: Just noticed that the OP now specifies that full precision is not required:
    perl -MMath::MPFR=:mpfr -le "Rmpfr_set_default_prec(28);print Math::MP +FR->new(2) ** 1024000;" 5.194693363e308254


    Cheers,
    Rob
Re: Efficiently working with huge exponents
by RichardK (Parson) on Mar 20, 2015 at 12:46 UTC

    Math::BigFloat can swap the library it uses, have you tried 'GMP' ?

    use Math::BigFloat lib => 'GMP';

    AFIACT that calls Math::BigInt::GMP so you'll need to install that too.

    use Math::BigInt; # or make it faster with huge numbers: install (optional) # Math::BigInt::GMP and always use (it will fall back to # pure Perl if the GMP library is not installed): # (See also the L<MATH LIBRARY> section!) # will warn if Math::BigInt::GMP cannot be found use Math::BigInt lib => 'GMP';
Re: Efficiently working with huge exponents
by LanX (Saint) on Mar 20, 2015 at 12:19 UTC
Re: Efficiently working with huge exponents
by Ea (Chaplain) on Mar 20, 2015 at 12:11 UTC
    Not quite what you asked, but how about transforming the problem domain and using logarithms? It's monotonic increasing, so the sort order remains the same. Different bases are scaled by an easily calculated factor and if you need the big integer, then you leave the calculation to the end.

    Sometimes I can think of 6 impossible LDAP attributes before breakfast.

      I thought of this as well, but I still need to be able to perform other arithmetic operations on the result, such as addition, multiplication, and even further exponentiation. Plus if I do $n->blog(10) to get the base-10 exponent, I still have to wait out the 3-minute calculation of $n in the first place, or maybe there's some clever math that I learned too many decades ago that would help. :-)

Re: Efficiently working with huge exponents (approx)
by tye (Sage) on Mar 20, 2015 at 13:39 UTC
Re: Efficiently working with huge exponents
by Laurent_R (Canon) on Mar 20, 2015 at 16:19 UTC
    Just a quick calculation in a one-liner:
    $ time perl -E 'my $c = log(2) * 1024000; my $d = $c / log(1e100); > $d = int $d; say $d; $c = $c - log(1e100) for 1..$d; > say $c; say "Value is: ", exp $c, " x 1e${d}00";' 3082 125.987232628425 Value is: 5.19469341155068e+54 x 1e308200 real 0m0.039s user 0m0.030s sys 0m0.000s
    The last decimals are certainly wrong, but it seems that we can say that 5.194693e+308254 is a fair approximation, obtained in 39 milliseconds.

    Update (at 16:36 UTC): A better version of more or less the same:

    $ time perl -E 'my $c = log(2) * 1024000; my $d = int ( $c / log(1e10 +0)); > say $d; $c = $c - $d * log(1e100); > say $c; say "Value is: ", exp $c, " x 1e${d}00";' 3082 125.987232618965 Value is: 5.19469336241126e+54 x 1e308200 real 0m0.036s user 0m0.000s sys 0m0.046s
    Update 2: fixed a wrong print out of the value.

    Je suis Charlie.

      Yeah, that's the same type of thing that Math::BigApprox makes much easier:

      $ time perl -MMath::BigApprox=c -le'print c(2)**1024000' 5.194693e+308254 real 0m0.021s user 0m0.012s sys 0m0.004s

      And it makes a good guess at how many decimal places to show as well.

      - tye        

        Thank you, tye ++, I did not know about this module until today, it looks very interesting, I'll look at it and at its code.

        I was only trying to give a very basic proof of concept to try to help the OP. Surely, your module offers much more than my poor basic attempts. Congrats for it.

        Je suis Charlie.

        Thanks to you both. While I'll probably just use Math::BigApprox for my current need, re-learning a little math never hurt anyone (much).

Re: Efficiently working with huge exponents
by BrowserUk (Patriarch) on Mar 20, 2015 at 15:21 UTC
    2**1024000

    Are all your big numbers powers of 2?


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". I'm with torvalds on this
    In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked

      The point I was getting at is that 2**1024000, is already logarithmic. Ie. 1024000 is log2 of your number.

      Thus, to convert it to log10(), you only need divide it by log2( 10 ).

      Then all you need is:

      sub log2{ log( $_[0] ) / log( 2 ) } sub log2ToLog10{ $_[0] / log2( 10 ) } print log2ToLog10( 1024000 );; 308254.715559917

      And once you have that, displaying it in human readable form becomes:

      my $log10 = log2ToLog10( 1024000 ); printf "%.16fe%d\n", 10**( $log10 - int( $log10 )), int( $log10 );; 5.1946933632179482e308254

      Which is pretty accurate according to wolfram/alpha:


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority". I'm with torvalds on this
      In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked

        A good observation and valid question, but, no, my calculations are not all powers of 2. Thanks for the math reminder. :-)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1120730]
Approved by ww
Front-paged by Old_Gray_Bear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (5)
As of 2024-04-25 10:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found