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}
-
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.Joachim Isaksson– Joachim Isaksson2016年02月19日 17:13:31 +00:00Commented Feb 19, 2016 at 17:13
2 Answers 2
You're working with NumPy float64 objects instead of Python float objects. This has a few consequences:
On Python 3,
float64objects are rounded using NumPy's rounding code instead of Python's. Python's rounding code forfloats always gives correctly-rounded results. NumPy's rounding code does not. (On Python 2, it's not possible to override theroundoperation for custom types, so when you round afloat64object in Python 2 it first gets converted to a Pythonfloat, and then Python's rounding code is used to give afloatresult. This is the main reason that you're seeing a difference between Python 2 and Python 3.)Again on Python 3, because of the above,
roundon afloat64gives afloat64result. On Python 2,roundon afloat64object gives afloatresult.Python
floatobjects have a differentrepr, which will give pleasanter output. In particular, Python's floatreprguarantees roundtripping for (not too large, not too small) decimal values with at most 15 significant figures: the representation of0.58is'0.58', for example. The representation of NumPyfloat64objects 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}
Comments
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.