-1

I thought this is a popular question but I have not been able to find an answer:

When square root of an integer is squared, I expect the result is an integer but I get the following:

from math import sqrt
from decimal import Decimal 

a = 5
b = sqrt(Decimal(a))
c = 2 * b ** 2
print(a, c)
if c.is_integer():
    print('c is integer')
    do something ...
else:
    print('c is NOT integer')

and the result:

5 10.000000000000002
c is NOT integer

I also tried without decimal.Decimal() but it did not work either. What is the best way to get around this? The above code is simplified for the question but I would like a generic solution if possible. I am using Python 3.5.

ddyke
  • 53
  • 6
  • 1
    How you would imagine that? Did you expect that the result is stored in symbolic form? – cubuspl42 Feb 09 '17 at 23:52
  • 2
    Wouldn't it be great if all floating point numbers were stored with infinite precision? [Too bad](http://stackoverflow.com/q/588004/5647260). – Andrew Li Feb 09 '17 at 23:52
  • 3
    sqrt doesn't return `int` it is `double` , it has to be double ... – Mohsen_Fatemi Feb 09 '17 at 23:53
  • 1
    It is *impossible* for a computer to render an accurate number for `sqrt(5)`, in base 2 or base 10. Rounding will happen, and you're seeing the result of that rounding. The only way around it is to do the math symbolically, e.g. with `sympy`. – Mark Ransom Feb 09 '17 at 23:53
  • That would be handy, if such a function exists, in a manner similar to fractions.Fraction() for handling fractions. At least I expect (sqrt(n))**2 returns n, but does not return integer either. – ddyke Feb 09 '17 at 23:58
  • So is simpy the only solution? – ddyke Feb 09 '17 at 23:59
  • @ddyke What type should the *result* of `sqrt(5)` be? – cubuspl42 Feb 10 '17 at 00:00
  • Possible [duplicate](http://stackoverflow.com/questions/21895756/why-are-floating-point-numbers-inaccurate), but I'm not sure – cubuspl42 Feb 10 '17 at 00:02
  • Possible duplicate of [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) –  Feb 10 '17 at 00:02
  • 1
    @ddyke I think that the most fundamental question is *why* you need accurate squares? Could you provide a little more context? – cubuspl42 Feb 10 '17 at 00:14
  • @cubuspl42 I just had a code in which I need to calculate sqrt of integers and later in the calculation it is squared back like the code in the question. The actual code takes another variable and the results could be an integer or a float. I intended to select integer for further processing. It seems that I have to optimise the formula to avoid sqrt() then square process. – ddyke Feb 10 '17 at 00:32
  • @ddyke So it's something like a class assignment? I was wondering why *specifically* the slight loss of precision is unacceptable. – cubuspl42 Feb 10 '17 at 00:34

2 Answers2

5

If you want an 'accurate' representation of sqrt(5) you'll have to acquaint yourself with a CAS or computer algebra system such as sympy.

>>> import sympy
>>> s = sympy.sqrt(5)
>>> s
sqrt(5)
>>> s*s
5
>>> type(s*s)
<class 'sympy.core.numbers.Integer'>
>>> float(s)
2.23606797749979

sqrt(5) is a so called irrational number. This means it cannot be written as a fraction. With their normal arithmetic computers can only store fractions and not even all of those, so on that road you are out of luck.

Here is a translation of your snippet to sympy, demonstrating the function simplify:

import sympy

def check_int(x, name='input'):
    print('ah', name, '=', x)
    sx = sympy.simplify(x)
    if sx != x:
        print('let me think...')
        print("that's", sx)
    if isinstance(sx, (int, sympy.Integer)):
        print(name, 'is integer')
    else:
        print(name, 'is NOT integer')
    print()

s = sympy.sqrt(5)
t = sympy.sqrt(3)
check_int(5)
check_int(s)
check_int(s**2)
check_int((s+1)**2)
check_int((s+1)**2-2*s)
check_int((s-t)**2)

Output:

ah input = 5
input is integer

ah input = sqrt(5)
input is NOT integer

ah input = 5
input is integer

ah input = (1 + sqrt(5))**2
input is NOT integer

ah input = -2*sqrt(5) + (1 + sqrt(5))**2
let me think...
that's 6
input is integer

ah input = (-sqrt(3) + sqrt(5))**2
let me think...
that's -2*sqrt(15) + 8
input is NOT integer
Paul Panzer
  • 51,835
  • 3
  • 54
  • 99
  • Thanks for the numpy example. The basis for my misunderstanding is that for example Excel 'properly' handles many types of irrational numbers, sqrt, pi, ln etc. and I simply expected there is a similar way to handle those irrational numbers at the language level in Python as well. I learned that I was wrong. – ddyke Feb 10 '17 at 00:40
  • @ddyke You're welcom make sure to not mix up `sympy` with `numpy`. I don't think Excel handles it properly, I'm almost certain they're just cheating, hiding the error by rounding. Don't worry, we all are always learning new things. Oh, and check out the updated post I've translated your snippet to sympy, if you like you can use that in your project. – Paul Panzer Feb 10 '17 at 00:52
  • @PaulPanzer You are right about Excel. See https://people.eecs.berkeley.edu/~wkahan/Mindless.pdf for details and https://en.wikipedia.org/wiki/William_Kahan for the author's creds. – John Machin Feb 10 '17 at 02:24
2

You should get familiar with how real numbers are stored in computers. Long story short, they either have a finite precision (and you just deal with it) or you need to do symbolic calculations (which is not used in everyday software and still has its limitations).

If in your specific problem domain you really need an accurate square root (and later accurate square of your square root), you could probably invent your own type SquareRoot and overload necessary operators. Please note that that won't work for whole math, like roots of third degree (x^(1/3)) or anything else.

cubuspl42
  • 7,833
  • 4
  • 41
  • 65
  • Thanks for the explanations, now I understand how Python handles non-fractional irrational numbers! Out of curiosity I played with R and it returns (sqrt(5))**2 as integer 5. So it seems that some languages handle those numbers differently at the language level. I guess other math-centered languages, e.g. mathematica, matlab, would behave that way. – ddyke Feb 10 '17 at 01:14
  • 1
    Actually it could mean two things: either language processes numbers symbolically or (what is more likely) it's printing mechanism is configured with too small precision to actually show the inaccuracy (like C++'s `std::cout` default). I don't know if either R or Matlab use symbolic computations by default, but I don't think they do. – cubuspl42 Feb 10 '17 at 01:32