8

I'd like to calculate the square root of a number bigger than 10^2000 in Python. If I treat this number like a normal integer, I will always get this result back:

Traceback (most recent call last):
  File "...", line 3, in <module>
    print( q*(0.5)  )
OverflowError: int too large to convert to float

How do I fix this? Or does a possibilty other than using Python exist to calculate this square root?

Gonçalo Peres
  • 11,752
  • 3
  • 54
  • 83
cubeAD
  • 83
  • 1
  • 1
  • 5

3 Answers3

14

Just use the decimal module:

>>> from decimal import *
>>> Decimal(10**2000).sqrt()
Decimal('1.000000000000000000000000000E+1000')
>>> Decimal(10**200000).sqrt()
Decimal('1.000000000000000000000000000E+100000')
>>> Decimal(15**35315).sqrt()
Decimal('6.782765081358674922386659760E+20766')

You can also use the gmpy2 library.

>>> import gmpy2
>>> n = gmpy2.mpz(99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999982920000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000726067)
>>> gmpy2.get_context().precision=2048
>>> x = gmpy2.sqrt(n)

Useful links:

  1. Decimal - Python Documentation
mrhallak
  • 1,138
  • 1
  • 12
  • 27
  • 3
    The `^` operator denotes XOR in Python. You should use `**`. You can see that on your false results, too. – clemens Dec 17 '17 at 11:36
  • 1
    There is no exponention operator in Java. The square root of 10^2000 is **never** 44.83... – clemens Dec 17 '17 at 11:39
  • If you are looking for an integer result and are going to use `gmpy2`, the simplest and best way is just `gmpy2.isqrt(). – casevh Dec 17 '17 at 16:09
11

The usual square root methods convert the parameter to a float value before doing the calculation. As you saw, this does not work well with very large integers.

So use a function that is designed to work on arbitrarily large integers. Here is one, guaranteed to return correct integer part of the square root of any positive integer. This function drops the fractional part of the result, which may or may not be what you want. Since this function uses iteration it is also slower than the built-in square root routines. The Decimal module works on larger integers than the built-in routines but the precision of the values must be defined in advance so it does not work on arbitrarily large values.

import math

_1_50 = 1 << 50  # 2**50 == 1,125,899,906,842,624

def isqrt(x):
    """Return the integer part of the square root of x, even for very
    large integer values."""
    if x < 0:
        raise ValueError('square root not defined for negative numbers')
    if x < _1_50:
        return int(math.sqrt(x))  # use math's sqrt() for small parameters
    n = int(x)
    if n <= 1:
        return n  # handle sqrt(0)==0, sqrt(1)==1
    # Make a high initial estimate of the result (a little lower is slower!!!)
    r = 1 << ((n.bit_length() + 1) >> 1)
    while True:
        newr = (r + n // r) >> 1  # next estimate by Newton-Raphson
        if newr >= r:
            return r
        r = newr
Rory Daulton
  • 21,934
  • 6
  • 42
  • 50
  • "guaranteed to return correct integer part of the square root of any positive integer" — it appears that it is not true for my case: `isqrt(178533196125860586848256)=422531887702 != math.sqrt(178533196125860586848256)=422531887703`. My calculator also gives 422531887703. I tried it with Python 3.5.2 x64 – Dmitry Aug 15 '19 at 19:13
  • 1
    @Dmitry: As I check your comment, I see that my `isqrt` is correct and the othe calculations are wrong. You can see that mine is correct by evaluating `422531887702**2 <= 178533196125860586848256 < 422531887703**2` in Python. That evaluates to `True`. However, `422531887703**2 <= 178533196125860586848256` evaluates to `False`. If the other methods say the correct square root is `422531887703` then they are wrong. Please check for yourself. If you find an actual error in my code, I would love to know, so please tell me. – Rory Daulton Aug 15 '19 at 21:21
  • @RoryDaulton thank you for the check. Yes, you are correct — your implementation gives accurate whole part while `math.sqrt` behaves very strange with big numbers: `math.sqrt(178533196125860602616209) == math.sqrt(178533196125860586848256)` returns True! – Dmitry Aug 16 '19 at 13:04
  • As far as I can see, the `if n <= 1` is always false because of the preceding `if x < _1_50: return int(math.sqrt(x))`. – kyrill Feb 18 '20 at 17:00
  • I'm getting a substantial discrepancy with this one: 1234592592962967901296297037045679133590224789902207663928489902170626521926687. Every large sqrt method that I try returns 1111122222333334444455555666667777799903. which I don't believe to be correct – J. Win. Oct 22 '20 at 16:40
  • 1
    @J.Win.: My `isqrt` returns a number different from the one you show. Your number ends `903` while my `isqrt` returned number ends `003`. The rest of the digits agree. My testing shows my result to be correct. Please double-check the results that you get and your tests on the results. – Rory Daulton Oct 23 '20 at 23:30
0

When using sqrt from the library math, before it proceeds to square root it, it will convert the value to a float.

If we manually try to convert the 10**2000 to a float, it also triggers an error

>>> float(10**2000)
---------------------------------------------------------------------------
OverflowError                             Traceback (most recent call last)
<ipython-input-14-6ac81f63106d> in <module>
----> 1 math.sqrt(10**2000)

OverflowError: int too large to convert to float

If we were speaking of a big number, but with the square equals or less than 308, the Decimal module would do the work as follows

>>> from decimal import Decimal
>>> Decimal(math.sqrt(10**308))
Decimal('10000000000000000369475456880582265409809179829842688451922778552150543659347219597216513109705408327446511753687232667314337003349573404171046192448274432')

However, as the number is way square is way bigger than 308, in this case, 2000, one would have to do as follows

>>> from decimal import Decimal
>>> Decimal(10**2000).sqrt()
Decimal('1.000000000000000000000000000E+1000')

Let's see the output if one tries to convert the Decimal(10**2000) to float

>>> float(Decimal(10**2000))
inf

One might also use the decimal module when working with factorials, as they tend to get large really fast.

Gonçalo Peres
  • 11,752
  • 3
  • 54
  • 83