three_tenths = .1 + .1 + .1
three_tenths

three_tenths == .3

"""
Explanation: math.isclose() example: Newton's method
Introducing math.isclose()
The math.isclose() function (new in Python 3.5) is the one obvious way™ to solve a common problem in floating point math: how to safely compare two values when we can't get exact equality due to the limitations of floating point arithmetic.
For example, consider this:
End of explanation
"""


import math

math.isclose(three_tenths, .3)

"""
Explanation: The issue is the efficient but approximate binary representation of some numbers (like .1) in modern CPUs using the IEEE-754 floating point standard. There are several ways of making such a comparison with a practical tolerance, but since Python 3.5 the canonical way is to use math.isclose(), like this:
End of explanation
"""


import math

def newton_sqrt(n, verbose=False):
    guess = n / 2
    while True:
        if verbose: print('guess ->', guess)
        better_guess = (guess + n/guess) / 2
        if math.isclose(guess, better_guess):
            return better_guess
        guess = better_guess

"""
Explanation: The math.isclose() function has two additional arguments to fine tune the tolerance, but I'll not cover them here.
Now let's see a real example using isclose().
Using math.isclose() with Newton's method
Newton's method of succesive approximations can be used to compute the square root. 

How Newton's method works
To compute sqrt(n), the algorithm starts with guess=n/2 and computes a better_guess: the average of guess and n/guess. If those two guesses are equal or very close, the square root is the better_guess. If not, the better_guess is used as the guess, and a new better_guess is computed as the average of that and n/guess. This process quickly converges to a very good approximation of the square root.

The implementation below uses math.is_close() to test whether the better_guess is close to the current one, which means further approximations will not be useful and the better_guess is an acceptable result.
End of explanation
"""


newton_sqrt(100)

"""
Explanation: Sample use:
End of explanation
"""


newton_sqrt(100, True)

"""
Explanation: Using verbose=True, we can see how quickly Newton's algorithm converges to the solution:
End of explanation
"""


for n in range(9, 17):
    computed = newton_sqrt(n)
    expected = math.sqrt(n)
    close = math.isclose(computed, expected)
    delta = computed - expected
    print('sqrt({:2d}): {:.20f} {:.20f} {} {:.20f}'.format(n, computed, expected, close, delta))

"""
Explanation: Applying newton_sqrt() to numbers from 9 to 16, we see that some results are a little different from those from math.sqrt(). However, the results are all considered close enough by isclose() with the default tolerance.
End of explanation
"""


from decimal import Decimal

one_tenth = Decimal('.1')
three_tenths = one_tenth + one_tenth + one_tenth
three_tenths == Decimal('.3')

"""
Explanation: Since Python already has a math.sqrt() function, our newton_sqrt() is only a didactic example. However, it is an elegant algorithm to know, and a fine example of the utility of math.isclose().
Bonus topic: the Decimal type
Another way of dealing with floating point issues is to completely avoid them by using the decimal.Decimal type, which can represent 0.1 with exact precision:
End of explanation
"""


Decimal(.1)

Decimal(.1) == one_tenth

"""
Explanation: Note the use of string arguments in the Decimal constructors above. Using a float to build a Decimal is often a bad idea, because, the Decimal value will reflect the imprecision of the float:
End of explanation
"""


math.isclose(Decimal(.1), one_tenth)

"""
Explanation: Again, the isclose function can be helpful:
End of explanation
"""


Decimal('.1') * 10

"""
Explanation: But if you need exact precision you should only use Decimal numbers built from strings or integers.
End of explanation
"""


