tags:

views:

330

answers:

2

Is there a way to get python to print extremely large longs in scientific notation? I am talking about numbers on the order of 10^1000 or larger, at this size the standard print "%e" % num fails.

For example:

Python 2.6.2 (release26-maint, Apr 19 2009, 01:56:41) 
[GCC 4.3.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print "%e" % 10**100
1.000000e+100
>>> print "%e" % 10**1000
Traceback (most recent call last):
  File "", line 1, in 
TypeError: float argument required, not long

It appears that python is trying to convert the long to a float and then print it, is it possible to get python to just print the long in scientific notation without converting it to a float?

+7  A: 

gmpy to the rescue...:

>>> import gmpy
>>> x = gmpy.mpf(10**1000)
>>> x.digits(10, 0, -1, 1)
'1.e1000'

I'm biased, of course, as the original author and still a committer of gmpy, but I do think it eases tasks such as this one that can be quite a chore without it (I don't know a simple way to do it without some add-on, and gmpy's definitely the add-on I'd choose here;-).

Alex Martelli
Not that it matter, in practical terms, but to push the understanding, do you have an insight into why the implicit conversion [to float] is not attempted on longs that are too long but does take place for the ones that fit in a float ?
mjv
In principle, convert to string and do some slicing etc, probably not *that* evil - but evil enough, certainly.
Steve314
@mjv - Python tries to treat numbers as numbers rather than as various distinct types - ie treating them as much the same as possible. It's part of a trend over a number of versions. In some respects, it's something I personally disagree with - particularly WRT changes in the behaviour of the division operator.
Steve314
@mjv, Python's `float`s respect (to the extent the underlying platform does;-) the IEEE standard for floating point; in particular, there's a limit on their magnitude, and `10**1000` is larger than the limit. @Steve314, the OP's problem here is exactly the reverse: longs are unlimited-size, floats aren't, so they're **not** "all numbers"; gmpy does offer (inter alia) unbounded-size (and unbounded-prespecified-precision) binary floating point numbers (Python's stdlib `decimal` module also does, for _decimal_ floating point numbers, btw).
Alex Martelli
Thanks Alex! I wish I didn't have to download a third-party module just to do this. Then again, GMPy looks interesting, are standard longs not implemented with GMP? I wonder if it might speed up my algorithms which produce these large numbers. Maybe I'll try it out.
sligocki
@Alex - that's why I said "to the extent possible", and why there two different things can happen. Python is trying to treat long and float the same - when the value of the long fits the standard float, it casts without fuss - otherwise, it stays as a long. This only makes sense if both are just different representations of one type - number. Treating them as distinct types would imply consistent behaviour - either consistently refuse the cast, or consistently apply it (and throw if it fails). Of course which approach is best is subjective opinion.
Steve314
@sligocki, no, the core Python runtime but yes, gmpy can indeed speed up computation on very large numbers (that **is** the main reason I wrote it, after all;-).
Alex Martelli
@Steve314, the ability to selectively have some value of a type cast to another in no way implies there's any attempt to "treat them the same" -- hey, just try indexing `mylist[1.0]`, which would of course be _quite_ within "the extent possible", and you'll see!-) Python's simply applying "practicality beats purity" by allowing _some_ interchangeability -- far less than "the extent possible", just cases that can be useful and can't hurt. And, it _does_ "throw if it fails" -- though we call it "raise" in Python; see the OP's TypeError...!
Alex Martelli
+1  A: 

No need to use a third party library. Here's a solution in Python3, that works for large integers.

def ilog(n, base):
    """
    Find the integer log of n with respect to the base.

    >>> import math
    >>> for base in range(2, 16 + 1):
    ...     for n in range(1, 1000):
    ...         assert ilog(n, base) == int(math.log(n, base) + 1e-10), '%s %s' % (n, base)
    """
    count = 0
    while n >= base:
        count += 1
        n //= base
    return count

def sci_notation(n, prec=3):
    """
    Represent n in scientific notation, with the specified precision.

    >>> sci_notation(1234 * 10**1000)
    '1.234e+1003'
    >>> sci_notation(10**1000 // 2, prec=1)
    '5.0e+999'
    """
    base = 10
    exponent = ilog(n, base)
    mantissa = n / base**exponent
    return '{0:.{1}f}e{2:+d}'.format(mantissa, prec, exponent)
Christian Oudard