On Sat, May 21, 2016 at 10:35 AM, Jon Ribbens <jon+use...@unequivocal.co.uk> wrote: > On 2016-05-20, Steven D'Aprano <st...@pearwood.info> wrote: >> By that logic, we ought to: >> >> - avoid using floats because their behaviour isn't intuitive and >> can be confusing; > > To be fair, I'm very sympathetic to that argument. I think programming > languages should never magically produce floats out of nowhere unless > the programmer has explicitly done "import float" or "float('3.23')" > or somesuch. They're misunderstood so often that any convenience > they provide is outweighed by the danger they bring. > > "(1/10) * (1/10) * 10 != (1/10)" anyone? I was distinctly unhappy with > the Python 3 "2/3 ~= 0.6666" thing and regard it as a very retrograde > change.
The trouble is, what SHOULD 2/3 return? * An integer? Makes a lot of sense to a C programmer. Not so much to someone who is expecting a nonzero value. This isn't terrible (hey, Python 2 managed with it no problem), but will definitely confuse a number of people. * A float? That's what we currently have. Not perfect, but it's going to confuse less people than 0 will. * A decimal.Decimal? That has its own issues (for instance, (x+y)/2 can return a value that isn't between x and y), and it still can't represent two thirds properly. * A fractions.Fraction? Well, at least that can perfectly represent a ratio of integers. But it plays REALLY badly with other non-integer types, and other operations than basic arithmetic (ever tried to take the square root of a fraction?), so it's really only suited to situations where you're working exclusively with fractions. * Something else? You say that Py3 making 1/10 => 0.1 was a "very retrograde change". Why? Yes, now you're seeing floats; but would you be less surprised by the Py2 version? Sure, Py2 has your little toy example working: >>> (1/10) * (1/10) * 10 == (1/10) True but that's because 1/10 is zero, not because it's been represented accurately! The biggest advantage of having integer division yield an integer is that it forces people to think about their data types; but since Python's float has a standing (core language support) that Decimal and Fraction don't have, I don't think it's a problem. It's the same with the complex type: >>> 2 ** 0.5 1.4142135623730951 >>> (-2) ** 0.5 (8.659560562354934e-17+1.4142135623730951j) Core data types will migrate between themselves as needed. (And this proves some of the inherent inaccuracies in floating point arithmetic; real number arithmetic says that the square root of -2 has a zero real part.) Interestingly, fractions.Fraction doesn't handle non-integer exponentiation, and punts to float: >>> fractions.Fraction(4) ** fractions.Fraction(2) Fraction(16, 1) >>> fractions.Fraction(4) ** fractions.Fraction(2, 3) 2.5198420997897464 >>> fractions.Fraction(4) ** fractions.Fraction(3, 2) 8.0 >>> fractions.Fraction(-4) ** fractions.Fraction(1, 2) (1.2246467991473532e-16+2j) Some data types just aren't well suited to certain operations. ChrisA -- https://mail.python.org/mailman/listinfo/python-list