On 29 July 2013 17:09, MRAB <pyt...@mrabarnett.plus.com> wrote: > On 29/07/2013 16:43, Steven D'Aprano wrote: >> >> Comparing floats to Fractions gives unexpected results:
You may not have expected these results but as someone who regularly uses the fractions module I do expect them. >> # Python 3.3 >> py> from fractions import Fraction >> py> 1/3 == Fraction(1, 3) >> False >> >> but: >> >> py> 1/3 == float(Fraction(1, 3)) >> True Why would you do the above? You're deliberately trying to create a float with a value that you know is not representable by the float type. The purpose of Fractions is precisely that they can represent all rational values, hence avoiding these problems. When I use Fractions my intention is to perform exact computation. I am very careful to avoid allowing floating point imprecision to sneak into my calculations. Mixing floats and fractions in computation is not IMO a good use of duck-typing. >> I expected that float-to-Fraction comparisons would convert the Fraction >> to a float, but apparently they do the opposite: they convert the float >> to a Fraction: >> >> py> Fraction(1/3) >> Fraction(6004799503160661, 18014398509481984) >> >> Am I the only one who is surprised by this? Is there a general rule for >> which way numeric coercions should go when doing such comparisons? I would say that if type A is a strict superset of type B then the coercion should be to type A. This is the case for float and Fraction since any float can be represented exactly as a Fraction but the converse is not true. > I'm surprised that Fraction(1/3) != Fraction(1, 3); after all, floats > are approximate anyway, and the float value 1/3 is more likely to be > Fraction(1, 3) than Fraction(6004799503160661, 18014398509481984). Refuse the temptation to guess: Fraction(float) should give the exact value of the float. It should not give one of the countably infinite number of other possible rational numbers that would (under a particular rounding scheme and the floating point format in question) round to the same float. If that is the kind of equality you would like to test for in some particular situation then you can do so by coercing to float explicitly. Calling Fraction(1/3) is a misunderstanding of what the fractions module is for and how to use it. The point is to guarantee avoiding floating point errors; this is impossible if you use floating point computations to initialise Fractions. Writing Fraction(1, 3) does look a bit ugly so my preferred way to reduce the boiler-plate in a script that uses lots of Fraction "literals" is to do: from fractions import Fraction as F # 1/3 + 1/9 + 1/27 + ... limit = F('1/3') / (1 - F('1/3')) That's not as good as dedicated syntax but with code highlighting it's still quite readable. Oscar -- http://mail.python.org/mailman/listinfo/python-list