0

Using Python 2.7.10, I have found quite by accident that 5*math.sqrt(3) and math.sqrt(5**2*3) are not the same float:

import math
import decimal

print decimal.Decimal(5*math.sqrt(3))
print decimal.Decimal(math.sqrt(5**2*3))
print 5*math.sqrt(3) == math.sqrt(5**2*3)

returns

8.660254037844385521793810767121613025665283203125
8.6602540378443872981506501673720777034759521484375
False

which shows that they differ on the 15th decimal place. Intriguingly, this does not happen for numbers neighboring 5 and 3. The following code show a few pairs of numbers for which the equality fails:

for j in range(1,10+1):
    for i in range(1,10+1):
        a = i*math.sqrt(j)
        b = math.sqrt(i**2*j)
        if not(a == b):
            print [i,j],

The list of problematic [i,j] pairs include: [3, 2] , [6, 2] , [9, 2] , [5, 3] , [9, 3] , [10, 3] , [3, 6] , [6, 6] , [7, 6] , [3, 8] , [6, 8] , [9, 8] , [5, 10] , [7, 10] , [10, 10]... Any ideas on why the rounding breaks, and why precisely for these pairs, and not others?

elkmaster
  • 51
  • 4
Thiago
  • 133
  • 7
  • What are you expecting Decimal to do? – Padraic Cunningham Apr 17 '16 at 21:30
  • 9
    While an interesting find, it's only because [floating point math is broken](http://stackoverflow.com/questions/588004/is-floating-point-math-broken). Neither of these values are "correct" - the difference may lie in intermediate rounding. – Jongware Apr 17 '16 at 21:33
  • math is known to not return accurate result with long decimal numbers. i doubt that the list of "problematic pairs" is accurate, more likely to be contextual – Claudiu Creanga Apr 17 '16 at 21:34
  • @PadraicCunningham Decimal places allowed me to see why these numbers differ. If you do not use Decimal you get visually the same floats, even though a == b still returns False. – Thiago Apr 17 '16 at 21:36
  • 1
    @PadraicCunningham Thanks! I'm new to Python. – Thiago Apr 17 '16 at 21:43
  • 3
    `math.sqrt(5**2*3)` is the most accurate: there is only one rounding step in the computation of `math.sqrt`. The alternative formula `5*math.sqrt(3)` involves a rounding step in the computation of `math.sqrt` and one after that in the multiplication. – Pascal Cuoq Apr 18 '16 at 08:45

1 Answers1

2

This is because floating point arithmetic is tricky. Neither of your results is actually correct. They have rounding issues because of the floating point and it looks weird because it's not rounding on powers of 10 but powers of 2.

If you need arbitrary precision arithmetic, you can use the mpmath module like this:

from mpmath import *

mp.dps=50
mp.pretty = True
sqrt3 = fmul(5, mp.sqrt(3))
sqrt75 = mp.sqrt(fmul(power(5,2), 3))
print "5*sqrt(3) =    ", sqrt3
print "sqrt(5**2*3) = ", sqrt75

That gives:

5*sqrt(3)    =  8.6602540378443864676372317075293618347140262690519
sqrt(5**2*3) =  8.6602540378443864676372317075293618347140262690519

The link provided by Rad Lexus is a good read on this topic.

Community
  • 1
  • 1
Eric Darchis
  • 24,537
  • 4
  • 28
  • 49
  • Investigating further, I get different results for `sqrt(75.0)` in C as well, using different methods. `math.h`'s native sqrt, and `sqrt13` in [this exhaustive overview](http://www.codeproject.com/Articles/69941/Best-Square-Root-Method-Algorithm-Function-Precisi) gives the second Python result, while `sqrt9` manages to give **exactly** the first Python result – all 48 digits! What about those Babylonians, eh? – Jongware Apr 17 '16 at 22:32
  • The rounding issues are not just "because of the floating point". The square roots of 3 and 75 are both irrational, so any rational number can only be an approximation. – Patricia Shanahan Apr 18 '16 at 00:55
  • @RadLexus “all 48 digits” it only computed the same double-precision number, because it was using double-precision computations. This is not so big a coincidence: as soon as the first 17 significant digits were the same, all the other digits after that were bound to be the same too. – Pascal Cuoq Apr 18 '16 at 08:50
  • @PascalCuoq: I was thinking of doing a much more in-depth comparison, using hex notation for reference. That may prove my gut feeling OP gets different results because the number of significant digits in `3` and `5**3*3` are different. – Jongware Apr 18 '16 at 08:54
  • 1
    @RadLexus The number of significant digits in 3 and 75 are not relevant: both these numbers are represented exactly as double-precision floating-point numbers. The number of significant digits in `math.sqrt(3)` (nearly the full 53 bits without checking) is relevant because it means there is no room to multiply by 5 exactly. – Pascal Cuoq Apr 18 '16 at 09:34