6

I stumpled into a test failure caused by floating point precision and am trying to understand it.

In short: Python3 round returns a different value depending on whether the type is a float or a numpy.float64 although I thought float==double==float64 and both Python3 and NumPy should round nearest to even.

Here the example:

npVal = np.float64(435)/100
pyVal = 435/100
print(round(npVal,1))             // 4.4
print(round(pyVal,1))             // 4.3
print(round(np.float64(pyVal),1)) // 4.4
print(round(float(npVal),1))      // 4.3

I understand that 4.35 and 4.4 might not be exactly representable in double but why is numpy round differently than Python although they both use the same datatypes and specify the function similar? I used the explicit division to avoid input rounding errors.

I don't know for sure, whether the double value for 4.35 is a bit more or less, so I can't say which of those implementations is (might be?) wrong.

There is a similar question: Strange behavior of numpy.round

There it was noted, that NumPy "rounds to the nearest even value" and "behaviour changed between Python 2 and Python 3; Python 3 behaves the same as NumPy here".

So both should do the same and round to nearest even value. So if 4.35 would be an exact float, 4.4 would the correct answer and needed to be returned by both.

6
  • They are both right, 4.35 is the same distance on the number line from 4.4 as it is from 4.3. You could try allowing a higher number of decimals to kick the can a little farther down the road. Commented Nov 12, 2018 at 18:05
  • Both implementations of round should round to even as explained in a comment to the linked question: "It's worth noting that this behaviour changed between Python 2 and Python 3; Python 3 behaves the same as NumPy here." I'm asking why this is not the case? Commented Nov 12, 2018 at 18:16
  • 1
    @coldspeed This isn't a duplicate: the other question was about Python 2, this question uses a print function, so is using Python 3, where they should have the same behaviour. Commented Nov 12, 2018 at 19:00
  • 2
    Python's round is correctly-rounded (but slow). NumPy's is not (but is faster). Different tradeoffs. (Agreed that this isn't a duplicate.) Commented Nov 12, 2018 at 19:01
  • @coldspeed: That question is not a duplicate. It says numpy.round rounds to even for values exactly halfway between rounded decimal values. But the npVal in this question cannot be exactly halfway between 4.3 and 4.4 because 4.35 is not representable in 64-bit binary floating-point. The closest representable value is 4.3499999999999996447286321199499070644378662109375, which ought to round down. Commented Nov 12, 2018 at 19:15

1 Answer 1

3

Calculating 435/100 in IEEE-754 basic 64-bit binary floating-point yields 4.3499999999999996447286321199499070644378662109375.

When this is rounded to the nearest decimal numeral with one digit after the decimal point, the result ought to be “4.3”. The Python rounding for this case appears to be correct.

For numpy.round, the documentation refers to numpy.around. The documentation for that says “Results may also be surprising due to … errors introduced when scaling by powers of ten.” Thus, it may be that numpy.round does not calculate the correct conversion of 4.3499999999999996447286321199499070644378662109375 to decimal but rather performs a 64-bit binary floating-point multiplication of that by 10, which yields exactly 43.5 due to floating-point rounding, and then numpy.round rounds that to 44 and formats it as “4.4”.

In summary, numpy.round is not a correct rounding routine.

Sign up to request clarification or add additional context in comments.

3 Comments

It seems you are correct for numpy: As far as I understand the code it does a multiply by 10^x and then a round to int and divide: github.com/numpy/numpy/blob/… This would be the only possible implementation of round to decimals I can think of. Any idea what Python3 does?
@Flamefire: Python is generally implemented by relying on the underlying platform. One can “easily” convert binary floating-point to exact decimal using techniques taught in elementary school, but straightforward implementations require more time and memory than is desired. Expert techniques to convert efficiently are known and published and are increasingly built into standard library functions such as C’s printf and scanf. (Doing it efficiently is interesting and somewhat hard.) But I could not tell you from my own knowledge what any particular Python implementations do.
@Flamefire: On most platforms (those where the underlying floating-point format can be identified as IEEE 754), CPython versions 2.7 and 3.1 and later use dedicated correctly-rounded binary-to-decimal and decimal-to-binary conversions, based on well-known code by David Gay. The key source is here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.