1

I have been set a challenge to calculate a number to 100 decimal points in python without using any imported modules e.g. to calculate square root of 2

As it is an academic challenge it means that I cannot import decimal, sympy, evalf, gmpy2 and other suggestions that would work from responses here how can i show an irrational number to 100 decimal places in python?

I have also tried to do calculations using range in steps of 0.0000000000001 etc., but that would require use of Numpy How to use a decimal range() step value?

When I do calculate square root of 2, Python only shows me 16 decimal places. 1.4142135623746899

I understand that there are reasons behind why Python won't do more decimal places https://docs.python.org/3.4/tutorial/floatingpoint.html

I have tried to think creatively and calculate the square root of 20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

but again Python does not return a complete number, I get an answer like this

1.414213562373095e+50

A solution that shows potential promise is using format

def mySqrt(x):

r = x
precision = 10 ** (-10)

while abs(x - r * r) > precision:
    r = (r + x / r) / 2

return r

find_square_root_of = 2
answer = format(mySqrt(find_square_root_of), ',.100f')
print(answer)

This gives me the answer of

1.4142135623746898698271934335934929549694061279296875000000000000000000000000000000000000000000000000

But I need the rest of the zeros calculated. Any suggestions on what needs fixing in the code?

Christopher
  • 427
  • 1
  • 8
  • 18
  • `format` only *displays* more digits but that doesn't make the underlying value more precise - this is the limit of floating-point precision. It seems your task is to implement an algorithm to calculate the digits! You could use the `decimal` module but you are not allowed to use it as it would defeat the purpose of the exercise. – mkrieger1 Oct 09 '20 at 10:23
  • Does this answer your question? [How to compute the digits of an irrational number one by one?](https://stackoverflow.com/questions/60318091/how-to-compute-the-digits-of-an-irrational-number-one-by-one) (It uses C but it doesn't really matter, the choice of algorithm is language-independent) – mkrieger1 Oct 09 '20 at 10:26
  • 1
    Use Newton's method to calculate the integer square root of `2*10**(2*n)` for some sufficiently large `n`. The result will be `sqrt(2)*10**n`. Then just print it out with a decimal point inserted at the appropriate point. You obviously can't use native floating point to do it, since it doesn't have nearly the needed precision. But if this is a challenge, doesn't that mean you're expected to solve it without help? – Tom Karzes Oct 09 '20 at 10:27
  • @mkrieger1 Your answer looks like it has potential, I am just seeing if I can find an online converter from C to Python – Christopher Oct 09 '20 at 10:58
  • @TomKarzes I am both a student of Python and in need of math tuition on Newton's method. I found the Newton's method that I am using above at https://hackernoon.com/calculating-the-square-root-of-a-number-using-the-newton-raphson-method-a-how-to-guide-yr4e32zo , for purposes of this exercise I cannot use the math import or sqrt function. I am trying but also struggling to implement your suggestion of 2*10**(2*n) in my code with the format option. – Christopher Oct 09 '20 at 11:04
  • def mySqrt(x): r = 2*10**(2*x) return r find_square_root_of = 2 answer = format(mySqrt(find_square_root_of), ',.100f') print(answer) – Christopher Oct 09 '20 at 11:05
  • Thank you for all your help. – Christopher Oct 09 '20 at 11:06

1 Answers1

3

Integers are natively arbitrary precision in python. You already had a good idea of finding the square root of 2*10**100, and gave an algorithm for doing so, but you are then using floating point numbers to implement that algorithm. If you use integers, then it should basically work (note that you would need 2*10**200 so that you are left with 101 digits after the square root). It will just need a little bit of tweaking for what happens as you approach the cutoff. Here is my suggestion:

x = 2 * 10 ** 200

r = x

def test_diffs(x, r):
    d0 = abs(x - r**2)
    dm = abs(x - (r-1)**2)
    dp = abs(x - (r+1)**2)
    minimised = d0 <= dm and d0 <= dp
    below_min = dp < dm
    return minimised, below_min

while True:
    oldr = r
    r = (r + x // r) // 2

    minimised, below_min = test_diffs(x, r)
    if minimised:
        break

    if r == oldr:
        if below_min:
            r += 1
        else:
            r -= 1
        minimised, _ = test_diffs(x, r)
        if minimised:
            break

print(f'{r // 10**100}.{r % 10**100:0100d}')

Gives:

1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727

For comparison:

>>> import decimal
>>> decimal.getcontext().prec=101
>>> Decimal("2").sqrt()
Decimal('1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727')
alani
  • 12,573
  • 2
  • 13
  • 23
  • thank you very much for your answer above, it definitely does achieve the desired result of showing 100 decimal places without importing any Python modules. I am trying to follow what all your code means. It appears that you managed to solve it with the algorithm, and use a print formatting style to show 100 decimal places - print(f'{r // 10**100}.{r % 10**100:0100d}') – Christopher Oct 09 '20 at 11:49
  • The print formatting is to split the number of multiples of `10**100` (becomes the integer part) from the modulo remainder `10**100` (becomes the fractional part); the latter is padded with leading zeros as required (although in this case the first digit after the decimal point is a 4, so none are needed). Obviously what you can't do is convert to a float while keeping this much precision, hence converting to an appropriately formatted string for printing. – alani Oct 09 '20 at 13:11