1

Why is the below rounding not working in python 3 but only in python 2?

random_nums = np.array([-1, 0, 1, 2, 3])
probabilities = np.array([0.01, 0.3, 0.58, 0.1, 0.01])
target = dict(zip(random_nums, probabilities))
target = {k: round(v, 2) for k, v in target.items()}
out:
{-1: 0.01,
 0: 0.29999999999999999,
 1: 0.57999999999999996,
 2: 0.10000000000000001,
 3: 0.01}
asked Feb 19, 2016 at 17:09
1
  • If you need exact rounding with floats/doubles, do the rounding when converting to string for output. Due to the number of digits displayed, that's as close as the chosen floating point precision gets to those numbers. Commented Feb 19, 2016 at 17:13

2 Answers 2

4

You're working with NumPy float64 objects instead of Python float objects. This has a few consequences:

  1. On Python 3, float64 objects are rounded using NumPy's rounding code instead of Python's. Python's rounding code for floats always gives correctly-rounded results. NumPy's rounding code does not. (On Python 2, it's not possible to override the round operation for custom types, so when you round a float64 object in Python 2 it first gets converted to a Python float, and then Python's rounding code is used to give a float result. This is the main reason that you're seeing a difference between Python 2 and Python 3.)

  2. Again on Python 3, because of the above, round on a float64 gives a float64 result. On Python 2, round on a float64 object gives a float result.

  3. Python float objects have a different repr, which will give pleasanter output. In particular, Python's float repr guarantees roundtripping for (not too large, not too small) decimal values with at most 15 significant figures: the representation of 0.58 is '0.58', for example. The representation of NumPy float64 objects does not have this property: it just prints out a result based on the most significant 17 decimal digits of the stored value.

So if you convert your values to Python float objects before the round, you'll see pleasanter output (and in some cases, the results may be a tiny bit more accurate). But note that just because the output looks nice, that doesn't mean that the results you're getting are exact. None of this changes the fact that 0.58 is not exactly representable as a Python float (or indeed a NumPy float64, which uses the same format). Remember, with binary floating-point, What You See Is Not What You Get.

Some example output on Python 3:

>>> random_nums = np.array([-1, 0, 1, 2, 3])
>>> probabilities = np.array([0.01, 0.3, 0.58, 0.1, 0.01])
>>> target = dict(zip(random_nums, probabilities))
>>> {k: round(v, 2) for k, v in target.items()}
{0: 0.29999999999999999, 1: 0.57999999999999996, 2: 0.10000000000000001, 3: 0.01, -1: 0.01}
>>> {k: round(float(v), 2) for k, v in target.items()}
{0: 0.3, 1: 0.58, 2: 0.1, 3: 0.01, -1: 0.01}
answered Feb 19, 2016 at 19:15
Sign up to request clarification or add additional context in comments.

Comments

0

Seems to be working fine. Remember, round returns a floating-point number.

It rounded to two decimals, but floats are inherently inprecise (for almost all the numbers), hence the output.

answered Feb 19, 2016 at 17:11

2 Comments

so the output looks nice and has only 2 decimals
If you want floating-point output, then that's the best you can do. There's nothing wrong with it. If you want to print it (string representation), that's another issue.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.