FGreg
FGreg

Reputation: 15330

How to retain precision when converting from float32 to float?

I have a numpy.float32 object that I want to encode as JSON. The problem is that when I convert to a native python float, I lose the precision of the value.

Example:

In [1]: import numpy as np
In [4]: np.float32(295.96).item()
Out[4]: 295.9599914550781

However, if I first convert to string, then to float, the precision is retained.

In [3]: float(str(np.float32(295.96)))
Out[3]: 295.96

Is there a way to retain my precision without having to go through a string first?

Why does str(np.float32(295.96)) seem to retain the precision but np.float32(295.96).item() (or float(np.float32(295.96)) or np.asscalar(np.float32(295.96))) does not?

Note: I cannot assume that the precision will always be .01. I need to retain the native precision of the data.

Upvotes: 2

Views: 1605

Answers (2)

Eric
Eric

Reputation: 97691

It's not possible to store 64 bits of precision in a 32-bit value. In python, float is 64 bit (what is referred to as a double in C). As a demo, everything is OK with 64-bit floats:

>>> d = 295.6; dn = np.float64(d)
>>> (d, dn)
(295.6, 295.95999999999998)  # numpy prints out more digits than python
>>> d == dn  # but these are still the same
True
>>> d - dn
0.0

But if you try and use 32 bits, you drop precision

>>> d = 295.96; fn = np.float32(d)
>>> (d, fn)
(295.96, 295.95999)
>>> d == fn
False
>>> d - fn
8.5449218545363692e-06

Why does str(np.float32(295.96)) seem to retain the precision

str(np.float32(295.96)) looks like it retains precision because np.float32.__str__ rounds (in base 10) for convenience. It just so happens that when rounded, it exactly matches the text you typed in your code. As a result, it has exactly the same value.

Upvotes: 2

freakish
freakish

Reputation: 56587

You incorrectly assume that what you see in console is what really happens. The result you see (i.e. the rounding) is just for you = the one who looks at the console. This is how printing of floats is implemented unfortunately (IMO confusing).

The underlying float is always correct, e.g.

In [4]: a = np.float32(295.96)
In [5]: a
Out[5]: 295.95999
In [6]: import json
In [7]: json.dumps(a.item())
Out[7]: '295.9599914550781'

Upvotes: 2

Related Questions