openlang

round(2.5) gives 2, and learners don't believe it

Learners keep guessing that round(2.5) is 3, and both languages I reach for say 2. Python gives the integer 2, Julia gives 2.0. Both send a tie to the even neighbour, so round(0.5) is 0 and round(1.5) is 2, and the pattern only shows once a few ties sit side by side.

print([round(x) for x in (0.5, 1.5, 2.5, 3.5)])

That prints [0, 2, 2, 4], and Julia gives the same values as floats. My hunch is that four ties at once teach better than stating the rule, because the 2 next to the other 2 is what looks broken, and then the question turns into why anyone would want this.

What I keep wondering is whether to add negative ties to the first example, where the sign gets involved too, or whether that only adds noise.

The conversation continues 4

Follow the reasoning
  1. Permalink

    The why is a count. Sum the four ties exactly and you get 8. Round every tie up first and you get 1 + 2 + 3 + 4 = 10. Round ties to even and you get 0 + 2 + 2 + 4 = 8. Rounding up pushes every tie the same way, so the error grows with the number of ties. To even sends as many down as up, and on average it cancels. A learner moving between languages will hit both, since Go's math.Round rounds ties away from zero and math.RoundToEven is the separate function.

  2. Permalink

    One thing to keep apart from this lesson, I think. round(2.675, 2) gives 2.67 in Python, and learners file it under the same surprise, but it has a different cause. The float closest to 2.675 sits a little below it, so there's no tie to break at all, and the Python docs use this very example. So ties first, with values that are exact in binary such as 0.5 and 2.5, and representation as a second step.

    from decimal import Decimal, ROUND_HALF_UP
    print(Decimal(2.675))
    print(Decimal('2.675').quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))

    The first line shows the stored value, 2.67499999999999982236431605997495353221893310546875. The second gives 2.68, because a string keeps the decimal value the learner actually typed.

  3. Permalink

    Python's round(-0.5) gives the int 0, while round(-0.5, 0) and Julia's round(-0.5) give -0.0. Nobody notices until Julia divides by it and hands back -Inf.

Other sides of the same question

  1. 3 + 4 * 2 is 14, and that is the easy part

    Everyone new to Smalltalk meets 3 + 4 * 2 early and gets told it's 14, because binary messages go left to right with no arith…

  2. Translating old CoffeeScript by hand is a decent JavaScript lesson

    An unfashionable exercise that works better than it has any right to. Give a learner a small old CoffeeScript file and have t…

  3. (x, x) does not mean both halves are equal

    Learners keep reading OCaml's (x, x) as "both halves are equal". It isn't that, and it isn't even legal. A newcomer tends to…

    Replies 5ocaml