String format doesn't handle correctly rounded Numpy Float32?

Viewed 104

I accidentally came into this weird behavior when use format string together with Numpy 32 float and Numpy.round() . To reproduce:

score=0.7827188774

npf32_score_rounded=np.round(np.float32(0.7827188774),4)
score_rounded=np.round(score,4)

print(f" {npf32_score_rounded}")
print(f" {score_rounded}")

print(npf32_score_rounded)
print(score_rounded)

#0.7827000021934509 expect to get 0.7827
#0.7827
#0.7827 # without string format, it works as expected
#0.7827

Is this behavior normal or it should be fixed?

Numpy version == 1.20.2

Python version == 3.8.0

1 Answers

Python's f-strings call the objects __format__ function. As can be seen in numpy's source code, for a floating point number this does a conversion to a Python float (64bit) before.


Since the value stored in the 32 bit float actually is 0.782700002193450927734375 (see IEE 754) for Python's higher precision float these decimal places are now significant. That's why those decimal places are now printed. Thanks @user2357112 supports Monica for pointing this out.

So it is the expected result.


import numpy as np

val = 0.7827188774

np_float_32 = np.round(np.float32(val), 4)
assert type(np_float_32) == np.float32

np_float_64 = np.round(val, 4)
assert type(np_float_64) == np.float64

# just calling print, no issues
print(np_float_32, np_float_64) # 0.7827 0.7827

# both str and repr work as intended
print(str(np_float_32), str(np_float_64)) # 0.7827 0.7827
print(repr(np_float_32), repr(np_float_64)) # 0.7827 0.7827

# behavior described by OP
print(f'{np_float_32} {np_float_64}') # 0.7827000021934509 0.7827

# f-string without format specification calls obj.__format__('')
# see https://github.com/numpy/numpy/blob/v1.20.2/numpy/core/src/multiarray/scalartypes.c.src#L273
# explicit conversion to python float
print(float(np_float_32), float(np_float_64)) # 0.7827000021934509 0.7827
Related