| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- """Machine limits for Float32 and Float64 and (long double) if available...
- """
- __all__ = ['finfo', 'iinfo']
- import math
- import types
- import warnings
- from functools import cached_property
- from numpy._utils import set_module
- from . import numeric, numerictypes as ntypes
- from ._multiarray_umath import _populate_finfo_constants
- def _fr0(a):
- """fix rank-0 --> rank-1"""
- if a.ndim == 0:
- a = a.copy()
- a.shape = (1,)
- return a
- def _fr1(a):
- """fix rank > 0 --> rank-0"""
- if a.size == 1:
- a = a.copy()
- a.shape = ()
- return a
- _convert_to_float = {
- ntypes.csingle: ntypes.single,
- ntypes.complex128: ntypes.float64,
- ntypes.clongdouble: ntypes.longdouble
- }
- # Parameters for creating MachAr / MachAr-like objects
- _title_fmt = 'numpy {} precision floating point number'
- _MACHAR_PARAMS = {
- ntypes.double: {
- 'itype': ntypes.int64,
- 'fmt': '%24.16e',
- 'title': _title_fmt.format('double')},
- ntypes.single: {
- 'itype': ntypes.int32,
- 'fmt': '%15.7e',
- 'title': _title_fmt.format('single')},
- ntypes.longdouble: {
- 'itype': ntypes.longlong,
- 'fmt': '%s',
- 'title': _title_fmt.format('long double')},
- ntypes.half: {
- 'itype': ntypes.int16,
- 'fmt': '%12.5e',
- 'title': _title_fmt.format('half')}}
- @set_module('numpy')
- class finfo:
- """
- finfo(dtype)
- Machine limits for floating point types.
- Attributes
- ----------
- bits : int
- The number of bits occupied by the type.
- dtype : dtype
- Returns the dtype for which `finfo` returns information. For complex
- input, the returned dtype is the associated ``float*`` dtype for its
- real and complex components.
- eps : float
- The difference between 1.0 and the next smallest representable float
- larger than 1.0. For example, for 64-bit binary floats in the IEEE-754
- standard, ``eps = 2**-52``, approximately 2.22e-16.
- epsneg : float
- The difference between 1.0 and the next smallest representable float
- less than 1.0. For example, for 64-bit binary floats in the IEEE-754
- standard, ``epsneg = 2**-53``, approximately 1.11e-16.
- iexp : int
- The number of bits in the exponent portion of the floating point
- representation.
- machep : int
- The exponent that yields `eps`.
- max : floating point number of the appropriate type
- The largest representable number.
- maxexp : int
- The smallest positive power of the base (2) that causes overflow.
- Corresponds to the C standard MAX_EXP.
- min : floating point number of the appropriate type
- The smallest representable number, typically ``-max``.
- minexp : int
- The most negative power of the base (2) consistent with there
- being no leading 0's in the mantissa. Corresponds to the C
- standard MIN_EXP - 1.
- negep : int
- The exponent that yields `epsneg`.
- nexp : int
- The number of bits in the exponent including its sign and bias.
- nmant : int
- The number of explicit bits in the mantissa (excluding the implicit
- leading bit for normalized numbers).
- precision : int
- The approximate number of decimal digits to which this kind of
- float is precise.
- resolution : floating point number of the appropriate type
- The approximate decimal resolution of this type, i.e.,
- ``10**-precision``.
- tiny : float
- An alias for `smallest_normal`, kept for backwards compatibility.
- smallest_normal : float
- The smallest positive floating point number with 1 as leading bit in
- the mantissa following IEEE-754 (see Notes).
- smallest_subnormal : float
- The smallest positive floating point number with 0 as leading bit in
- the mantissa following IEEE-754.
- Parameters
- ----------
- dtype : float, dtype, or instance
- Kind of floating point or complex floating point
- data-type about which to get information.
- See Also
- --------
- iinfo : The equivalent for integer data types.
- spacing : The distance between a value and the nearest adjacent number
- nextafter : The next floating point value after x1 towards x2
- Notes
- -----
- For developers of NumPy: do not instantiate this at the module level.
- The initial calculation of these parameters is expensive and negatively
- impacts import times. These objects are cached, so calling ``finfo()``
- repeatedly inside your functions is not a problem.
- Note that ``smallest_normal`` is not actually the smallest positive
- representable value in a NumPy floating point type. As in the IEEE-754
- standard [1]_, NumPy floating point types make use of subnormal numbers to
- fill the gap between 0 and ``smallest_normal``. However, subnormal numbers
- may have significantly reduced precision [2]_.
- For ``longdouble``, the representation varies across platforms. On most
- platforms it is IEEE 754 binary128 (quad precision) or binary64-extended
- (80-bit extended precision). On PowerPC systems, it may use the IBM
- double-double format (a pair of float64 values), which has special
- characteristics for precision and range.
- This function can also be used for complex data types as well. If used,
- the output will be the same as the corresponding real float type
- (e.g. numpy.finfo(numpy.csingle) is the same as numpy.finfo(numpy.single)).
- However, the output is true for the real and imaginary components.
- References
- ----------
- .. [1] IEEE Standard for Floating-Point Arithmetic, IEEE Std 754-2008,
- pp.1-70, 2008, https://doi.org/10.1109/IEEESTD.2008.4610935
- .. [2] Wikipedia, "Denormal Numbers",
- https://en.wikipedia.org/wiki/Denormal_number
- Examples
- --------
- >>> import numpy as np
- >>> np.finfo(np.float64).dtype
- dtype('float64')
- >>> np.finfo(np.complex64).dtype
- dtype('float32')
- """
- _finfo_cache = {}
- __class_getitem__ = classmethod(types.GenericAlias)
- def __new__(cls, dtype):
- try:
- obj = cls._finfo_cache.get(dtype) # most common path
- if obj is not None:
- return obj
- except TypeError:
- pass
- if dtype is None:
- # Deprecated in NumPy 1.25, 2023-01-16
- warnings.warn(
- "finfo() dtype cannot be None. This behavior will "
- "raise an error in the future. (Deprecated in NumPy 1.25)",
- DeprecationWarning,
- stacklevel=2
- )
- try:
- dtype = numeric.dtype(dtype)
- except TypeError:
- # In case a float instance was given
- dtype = numeric.dtype(type(dtype))
- obj = cls._finfo_cache.get(dtype)
- if obj is not None:
- return obj
- dtypes = [dtype]
- newdtype = ntypes.obj2sctype(dtype)
- if newdtype is not dtype:
- dtypes.append(newdtype)
- dtype = newdtype
- if not issubclass(dtype, numeric.inexact):
- raise ValueError(f"data type {dtype!r} not inexact")
- obj = cls._finfo_cache.get(dtype)
- if obj is not None:
- return obj
- if not issubclass(dtype, numeric.floating):
- newdtype = _convert_to_float[dtype]
- if newdtype is not dtype:
- # dtype changed, for example from complex128 to float64
- dtypes.append(newdtype)
- dtype = newdtype
- obj = cls._finfo_cache.get(dtype, None)
- if obj is not None:
- # the original dtype was not in the cache, but the new
- # dtype is in the cache. we add the original dtypes to
- # the cache and return the result
- for dt in dtypes:
- cls._finfo_cache[dt] = obj
- return obj
- obj = object.__new__(cls)._init(dtype)
- for dt in dtypes:
- cls._finfo_cache[dt] = obj
- return obj
- def _init(self, dtype):
- self.dtype = numeric.dtype(dtype)
- self.bits = self.dtype.itemsize * 8
- self._fmt = None
- self._repr = None
- _populate_finfo_constants(self, self.dtype)
- return self
- @cached_property
- def epsneg(self):
- # Assume typical floating point logic. Could also use nextafter.
- return self.eps / self._radix
- @cached_property
- def resolution(self):
- return self.dtype.type(10)**-self.precision
- @cached_property
- def machep(self):
- return int(math.log2(self.eps))
- @cached_property
- def negep(self):
- return int(math.log2(self.epsneg))
- @cached_property
- def nexp(self):
- # considering all ones (inf/nan) and all zeros (subnormal/zero)
- return math.ceil(math.log2(self.maxexp - self.minexp + 2))
- @cached_property
- def iexp(self):
- # Calculate exponent bits from it's range:
- return math.ceil(math.log2(self.maxexp - self.minexp))
- def __str__(self):
- if (fmt := getattr(self, "_fmt", None)) is not None:
- return fmt
- def get_str(name, pad=None):
- if (val := getattr(self, name, None)) is None:
- return "<undefined>"
- if pad is not None:
- s = str(val).ljust(pad)
- return str(val)
- precision = get_str("precision", 3)
- machep = get_str("machep", 6)
- negep = get_str("negep", 6)
- minexp = get_str("minexp", 6)
- maxexp = get_str("maxexp", 6)
- resolution = get_str("resolution")
- eps = get_str("eps")
- epsneg = get_str("epsneg")
- tiny = get_str("tiny")
- smallest_normal = get_str("smallest_normal")
- smallest_subnormal = get_str("smallest_subnormal")
- nexp = get_str("nexp", 6)
- max_ = get_str("max")
- if hasattr(self, "min") and hasattr(self, "max") and -self.min == self.max:
- min_ = "-max"
- else:
- min_ = get_str("min")
- fmt = (
- f'Machine parameters for {self.dtype}\n'
- f'---------------------------------------------------------------\n'
- f'precision = {precision} resolution = {resolution}\n'
- f'machep = {machep} eps = {eps}\n'
- f'negep = {negep} epsneg = {epsneg}\n'
- f'minexp = {minexp} tiny = {tiny}\n'
- f'maxexp = {maxexp} max = {max_}\n'
- f'nexp = {nexp} min = {min_}\n'
- f'smallest_normal = {smallest_normal} '
- f'smallest_subnormal = {smallest_subnormal}\n'
- f'---------------------------------------------------------------\n'
- )
- self._fmt = fmt
- return fmt
- def __repr__(self):
- if (repr_str := getattr(self, "_repr", None)) is not None:
- return repr_str
- c = self.__class__.__name__
- # Use precision+1 digits in exponential notation
- fmt_str = _MACHAR_PARAMS.get(self.dtype.type, {}).get('fmt', '%s')
- if fmt_str != '%s' and hasattr(self, 'max') and hasattr(self, 'min'):
- max_str = (fmt_str % self.max).strip()
- min_str = (fmt_str % self.min).strip()
- else:
- max_str = str(self.max)
- min_str = str(self.min)
- resolution_str = str(self.resolution)
- repr_str = (f"{c}(resolution={resolution_str}, min={min_str},"
- f" max={max_str}, dtype={self.dtype})")
- self._repr = repr_str
- return repr_str
- @cached_property
- def tiny(self):
- """Return the value for tiny, alias of smallest_normal.
- Returns
- -------
- tiny : float
- Value for the smallest normal, alias of smallest_normal.
- Warns
- -----
- UserWarning
- If the calculated value for the smallest normal is requested for
- double-double.
- """
- return self.smallest_normal
- @set_module('numpy')
- class iinfo:
- """
- iinfo(type)
- Machine limits for integer types.
- Attributes
- ----------
- bits : int
- The number of bits occupied by the type.
- dtype : dtype
- Returns the dtype for which `iinfo` returns information.
- min : int
- The smallest integer expressible by the type.
- max : int
- The largest integer expressible by the type.
- Parameters
- ----------
- int_type : integer type, dtype, or instance
- The kind of integer data type to get information about.
- See Also
- --------
- finfo : The equivalent for floating point data types.
- Examples
- --------
- With types:
- >>> import numpy as np
- >>> ii16 = np.iinfo(np.int16)
- >>> ii16.min
- -32768
- >>> ii16.max
- 32767
- >>> ii32 = np.iinfo(np.int32)
- >>> ii32.min
- -2147483648
- >>> ii32.max
- 2147483647
- With instances:
- >>> ii32 = np.iinfo(np.int32(10))
- >>> ii32.min
- -2147483648
- >>> ii32.max
- 2147483647
- """
- _min_vals = {}
- _max_vals = {}
- __class_getitem__ = classmethod(types.GenericAlias)
- def __init__(self, int_type):
- try:
- self.dtype = numeric.dtype(int_type)
- except TypeError:
- self.dtype = numeric.dtype(type(int_type))
- self.kind = self.dtype.kind
- self.bits = self.dtype.itemsize * 8
- self.key = "%s%d" % (self.kind, self.bits)
- if self.kind not in 'iu':
- raise ValueError(f"Invalid integer data type {self.kind!r}.")
- @property
- def min(self):
- """Minimum value of given dtype."""
- if self.kind == 'u':
- return 0
- else:
- try:
- val = iinfo._min_vals[self.key]
- except KeyError:
- val = int(-(1 << (self.bits - 1)))
- iinfo._min_vals[self.key] = val
- return val
- @property
- def max(self):
- """Maximum value of given dtype."""
- try:
- val = iinfo._max_vals[self.key]
- except KeyError:
- if self.kind == 'u':
- val = int((1 << self.bits) - 1)
- else:
- val = int((1 << (self.bits - 1)) - 1)
- iinfo._max_vals[self.key] = val
- return val
- def __str__(self):
- """String representation."""
- fmt = (
- 'Machine parameters for %(dtype)s\n'
- '---------------------------------------------------------------\n'
- 'min = %(min)s\n'
- 'max = %(max)s\n'
- '---------------------------------------------------------------\n'
- )
- return fmt % {'dtype': self.dtype, 'min': self.min, 'max': self.max}
- def __repr__(self):
- return "%s(min=%s, max=%s, dtype=%s)" % (self.__class__.__name__,
- self.min, self.max, self.dtype)
|