Unexpected behaviour of math.floor, round and int functions (rounding)

Chris Angelico rosuav at gmail.com
Sat Nov 20 17:04:31 EST 2021


On Sun, Nov 21, 2021 at 6:51 AM Ben Bacarisse <ben.usenet at bsb.me.uk> wrote:
>
> Chris Angelico <rosuav at gmail.com> writes:
>
> > On Sat, Nov 20, 2021 at 3:41 PM Ben Bacarisse <ben.usenet at bsb.me.uk> wrote:
> >>
> >> Chris Angelico <rosuav at gmail.com> writes:
> >>
> >> > On Sat, Nov 20, 2021 at 12:43 PM Ben Bacarisse <ben.usenet at bsb.me.uk> wrote:
> >> >>
> >> >> Chris Angelico <rosuav at gmail.com> writes:
> >> >>
> >> >> > On Sat, Nov 20, 2021 at 9:07 AM Ben Bacarisse <ben.usenet at bsb.me.uk> wrote:
> >> >> >>
> >> >> >> Chris Angelico <rosuav at gmail.com> writes:
> >> >> >>
> >> >> >> > On Sat, Nov 20, 2021 at 5:08 AM ast <ast at invalid> wrote:
> >> >> >>
> >> >> >> >>  >>> 0.3 + 0.3 + 0.3 == 0.9
> >> >> >> >> False
> >> >> >> >
> >> >> >> > That's because 0.3 is not 3/10. It's not because floats are
> >> >> >> > "unreliable" or "inaccurate". It's because the ones you're entering
> >> >> >> > are not what you think they are.
> >> >> >> >
> >> >> >> > When will people understand this?
> >> >> >> >
> >> >> >> > (Probably never. Sigh.)
> >> >> >>
> >> >> >> Most people understand what's going on when it's explained to them.  And
> >> >> >> I think that being initially baffled is not unreasonable.  After all,
> >> >> >> almost everyone comes to computers after learning that 3/10 can be
> >> >> >> written as 0.3.  And Python "plays along" with the fiction to some
> >> >> >> extent.  0.3 prints as 0.3, 3/10 prints as 0.3 and 0.3 == 3/10 is True.
> >> >> >
> >> >> > In grade school, we learn that not everything can be written that way,
> >> >> > and 1/3 isn't actually equal to 0.3333333333.
> >> >>
> >> >> Yes.  We learn early on that 0.3333333333 means 3333333333/10000000000.
> >> >> We don't learn that 0.3333333333 is a special notation for machines that
> >> >> have something called "binary floating point hardware" that does not
> >> >> mean 3333333333/10000000000.  That has to be learned later.  And every
> >> >> generation has to learn it afresh.
> >> >
> >> > But you learn that it isn't the same as 1/3. That's my point. You
> >> > already understand that it is *impossible* to write out 1/3 in
> >> > decimal. Is it such a stretch to discover that you cannot write 3/10
> >> > in binary?
> >> >
> >> > Every generation has to learn about repeating fractions, but most of
> >> > us learn them in grade school. Every generation learns that computers
> >> > talk in binary. Yet, putting those two concepts together seems beyond
> >> > many people, to the point that they feel that floating point can't be
> >> > trusted.
> >>
> >> Binary is a bit of a red herring here.  It's the floating point format
> >> that needs to be understood.  Three tenths can be represented in many
> >> binary formats, and even decimal floating point will have some surprises
> >> for the novice.
> >
> > Not completely a red herring; binary floating-point as used in Python
> > (IEEE double-precision) is defined as a binary mantissa and a scale,
> > just as "blackboard arithmetic" is generally defined as a decimal
> > mantissa and a scale. (At least, I don't think I've ever seen anyone
> > doing arithmetic on a blackboard in hex or octal.)
>
> You seem to be agreeing with me.  It's the floating point part that is
> the issue, not the base itself.

Mostly, but all the problems come from people expecting decimal floats
when they're using binary floats.

> >> >> Yes, agreed, but I was not commenting on the odd (and incorrect) view
> >> >> that floating point operations are not reliable and well-defined, but on
> >> >> the reasonable assumption that a clever programming language might take
> >> >> 0.3 to mean what I was taught it meant in grade school.
> >> >
> >> > It does mean exactly what it meant in grade school, just as 1/3 means
> >> > exactly what it meant in grade school. Now try to represent 1/3 on a
> >> > blackboard, as a decimal fraction. If that's impossible, does it mean
> >> > that 1/3 doesn't mean 1/3, or that 1/3 can't be represented?
> >>
> >> As you know, it is possible, but let's say we outlaw any finite notation
> >> for repeated digits...  Why should I convert 1/3 to this particular
> >> apparently unsuitable representation?  I will write 1/3 and manipulate
> >> that number using factional notation.
> >
> > If you want that, the fractions module is there for you.
>
> Yes, I know.  The only point of disagreement (as far as can see) is
> that literals like 0.3 appears to be confusing for beginners.  You think
> they should know that "binary" (which may be all they know about
> computers and numbers) means fixed-width binary floating point (or at
> least might imply a format that can't represent three tenths), where I
> think it's not unreasonable for them to suppose that 0.3 is manipulated
> as the rational number it so clearly is.

Rationals are mostly irrelevant. We don't use int/int for most
purposes. When you're comparing number systems between the way people
write them and the way computers do, the difference isn't "0.3" and
"3/10". If people are prepared to switch their thinking to rationals
instead of decimals, then sure, the computer can represent those
precisely; but that has different tradeoffs.

> > And again,
> > grade school, we learned about ratios as well as decimals (or vulgar
> > fractions and decimal fractions). They have different tradeoffs. For
> > instance, I learned pi as both 22/7 and 3.14, because sometimes it'd
> > be convenient to use the rational form and other times the decimal.
> >
> >> The novice programmer might similarly expect that when they write 0.3,
> >> the program will manipulate that number as the faction it clearly is.
> >> They may well be surprised by the fact that it must get put into a
> >> format that can't represent what those three characters mean, just as I
> >> would be surprised if you insisted I write 1/3 as a finite decimal (with
> >> no repeat notation).
> >
> > Except that 0.3 isn't written as a fraction, it's written as a
> > decimal.
>
> Are you worrying about the term I used?  0.3 is a rational number.  It
> is the way to write a particular fraction in decimal.  I think the only
> point of disagreement is that you hope or expect that anyone writing
> three tenths as 0.3 should expect that that literal might be converted
> to some collection of bits that can't represent it.  I think it's not
> unreasonable for a beginner to think otherwise.

0.3 is a "rational number" but it's not being written as a fraction.
And most people aren't going to think of it as "3/10". The problem
here isn't mathematics, it's people, so the point isn't whether a
number could be considered rational in the abstract.

> >> I'm not saying your analogy would not help someone understand, but you
> >> first have to explain why 0.3 is not treated as three tenths -- why I
> >> (to use your analogy) must not keep 1/3 as a proper fraction, but I must
> >> instead write it using a finite number of decimal digits.  Neither is,
> >> in my view, obvious to the beginner.
> >
> > Try adding 1/3 + e; either you have to convert 1/3 to a decimal, or
> > find a rational approximation for e (there aren't any really good ones
> > but 193/71 seems promising - that's 2.7183, close enough) and then go
> > to the work of rational addition. No, more likely you'll go for a
> > finite number of decimal digits.
>
> Of course, but I'm not sure how this adds to the discussion.  e is not
> like three tenths.  Most programming languages give us a way to write
> what looks like exactly three tenths, but most don't give us a way write
> a literal that looks like it means e exactly.
>

That's, once again, because people. People want a way to say "give me
a value that's as close as possible to 0.3" and so that's what we get.
And then they get caught out by the fact that it isn't what they want
it to be.

ChrisA


More information about the Python-list mailing list