What exactly is the "resolution" parameter of numpy float
The short answer is "dont' confuse numpy.finfo
with numpy.spacing
".
finfo
operates on the dtype
, while spacing
operates on the value.
Background Information
First, though, some general explanation:
The key part to understand is that floating point numbers are similar to scientific notation. Just like you'd write 0.000001 as 1.0 x 10^-6
, floats are similar to c x 2^q
. In other words, they have two separate parts - a coefficient (c
, a.k.a. "significand") and an exponent (q
). These two values are stored as integers.
Therefore, how closely a value can be represented (let's think of this as the degree of discretization) is a function of both parts, and depends on the magnitude of the value.
However, the "precision" (as referred to by np.finfo
) is essentially the number of significant digits if the number were written in base-10 scientific notation. The "resolution" is the resolution of the coefficient (part in front) if the value were written in the same base-10 scientific notation (i.e. 10^-precision
). In other words, both are only a function of the coefficient.
Numpy-specific
For numpy.finfo
, "precision" and "resolution" are simply the inverse of each other. Neither one tells you how closely a particular number is being represented. They're purely a function of the dtype
.
Instead, if you're worried about the absolute degree of discretization, use numpy.spacing(your_float)
. This will return the difference in the next largest value in that particular format (e.g. it's different for a float32
than a float64
).
Examples
As an example:
In [1]: import numpy as npIn [2]: np.spacing(10.1)Out[2]: 1.7763568394002505e-15In [3]: np.spacing(10000000000.1)Out[3]: 1.9073486328125e-06In [4]: np.spacing(1000000000000.1)Out[4]: 0.0001220703125In [5]: np.spacing(100000000000000.1)Out[5]: 0.015625In [6]: np.spacing(10000000000000000.1)Out[6]: 2.0
But the precision and resolution don't change:
In [7]: np.finfo(10.1).precisionOut[7]: 15In [8]: np.finfo(10000000000000000.1).precisionOut[8]: 15In [9]: np.finfo(10.1).resolutionOut[9]: 1.0000000000000001e-15In [10]: np.finfo(10000000000000000000.1).resolutionOut[10]: 1.0000000000000001e-15
Also note that all of these depend on the data type that you're using:
In [11]: np.spacing(np.float32(10.1))Out[11]: 9.5367432e-07In [12]: np.spacing(np.float32(10000000000000.1))Out[12]: 1048576.0In [13]: np.finfo(np.float32).precisionOut[13]: 6In [14]: np.finfo(np.float32).resolutionOut[14]: 1e-06In [15]: np.spacing(np.float128(10.1))Out[15]: 8.6736173798840354721e-19In [16]: np.spacing(np.float128(10000000000000.1))Out[16]: 9.5367431640625e-07In [17]: np.finfo(np.float128).precisionOut[17]: 18In [18]: np.finfo(np.float128).resolutionOut[18]: 1.0000000000000000007e-18
Specific Questions
Now on to your specific questions:
But practically, does it mean that I should expect results to be erroneous if I preform operations using numbers less than the resolution?
No, because the precision/resolution (in numpy.finfo
terms) is only a function of the coefficient, and doesn't take into account the exponent. Very small and very large numbers have the same "precision", but that's not an absolute "error".
As a rule of thumb, when using the "resolution" or "precision" terms from finfo
, think of scientific notation. If we're operating on small numbers with similar magnitudes, we don't need to worry about much.
Let's take the decimal math case with 6 significant digits (somewhat similar to a float32
):
1.20000 x 10^-19 + 2.50000 x 10^-20 => 1.45000 x 10^19
However, if we operate on numbers with wildly different magnitudes but limited precision (again, 6 significant digits):
1.20000 x 10^6 + 2.50000 x 10^-5 => 1.20000
We'll start to see the effects quite clearly.
How can I quantify the error, for say addition, of two floating point numbers given their precision?
Use np.spacing(result)
.
If the resolution is as "large" as 1e-15, why would the smallest allowable number be on the order of 1e-308?
Again, the "resolution" in this case doesn't take into account the exponent, just the part in front.
Hopefully that helps clarify things somewhat. All of this is a bit confusing, and everyone gets bitten by it at some point. It's good to try to build up a bit of intuition about it and to know what functions to call to find out exactly in your platform-of-choice!