123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718 |
- """Machine limits for Float32 and Float64 and (long double) if available...
- """
- __all__ = ['finfo', 'iinfo']
- import warnings
- from ._machar import MachAr
- from .overrides import set_module
- from . import numeric
- from . import numerictypes as ntypes
- from .numeric import array, inf, NaN
- from .umath import log10, exp2, nextafter, isnan
- 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
- class MachArLike:
- """ Object to simulate MachAr instance """
- def __init__(self, ftype, *, eps, epsneg, huge, tiny,
- ibeta, smallest_subnormal=None, **kwargs):
- self.params = _MACHAR_PARAMS[ftype]
- self.ftype = ftype
- self.title = self.params['title']
- # Parameter types same as for discovered MachAr object.
- if not smallest_subnormal:
- self._smallest_subnormal = nextafter(
- self.ftype(0), self.ftype(1), dtype=self.ftype)
- else:
- self._smallest_subnormal = smallest_subnormal
- self.epsilon = self.eps = self._float_to_float(eps)
- self.epsneg = self._float_to_float(epsneg)
- self.xmax = self.huge = self._float_to_float(huge)
- self.xmin = self._float_to_float(tiny)
- self.smallest_normal = self.tiny = self._float_to_float(tiny)
- self.ibeta = self.params['itype'](ibeta)
- self.__dict__.update(kwargs)
- self.precision = int(-log10(self.eps))
- self.resolution = self._float_to_float(
- self._float_conv(10) ** (-self.precision))
- self._str_eps = self._float_to_str(self.eps)
- self._str_epsneg = self._float_to_str(self.epsneg)
- self._str_xmin = self._float_to_str(self.xmin)
- self._str_xmax = self._float_to_str(self.xmax)
- self._str_resolution = self._float_to_str(self.resolution)
- self._str_smallest_normal = self._float_to_str(self.xmin)
- @property
- def smallest_subnormal(self):
- """Return the value for the smallest subnormal.
- Returns
- -------
- smallest_subnormal : float
- value for the smallest subnormal.
- Warns
- -----
- UserWarning
- If the calculated value for the smallest subnormal is zero.
- """
- # Check that the calculated value is not zero, in case it raises a
- # warning.
- value = self._smallest_subnormal
- if self.ftype(0) == value:
- warnings.warn(
- 'The value of the smallest subnormal for {} type '
- 'is zero.'.format(self.ftype), UserWarning, stacklevel=2)
- return self._float_to_float(value)
- @property
- def _str_smallest_subnormal(self):
- """Return the string representation of the smallest subnormal."""
- return self._float_to_str(self.smallest_subnormal)
- def _float_to_float(self, value):
- """Converts float to float.
- Parameters
- ----------
- value : float
- value to be converted.
- """
- return _fr1(self._float_conv(value))
- def _float_conv(self, value):
- """Converts float to conv.
- Parameters
- ----------
- value : float
- value to be converted.
- """
- return array([value], self.ftype)
- def _float_to_str(self, value):
- """Converts float to str.
- Parameters
- ----------
- value : float
- value to be converted.
- """
- return self.params['fmt'] % array(_fr0(value)[0], self.ftype)
- _convert_to_float = {
- ntypes.csingle: ntypes.single,
- ntypes.complex_: ntypes.float_,
- ntypes.clongfloat: ntypes.longfloat
- }
- # Parameters for creating MachAr / MachAr-like objects
- _title_fmt = 'numpy {} precision floating point number'
- _MACHAR_PARAMS = {
- ntypes.double: dict(
- itype = ntypes.int64,
- fmt = '%24.16e',
- title = _title_fmt.format('double')),
- ntypes.single: dict(
- itype = ntypes.int32,
- fmt = '%15.7e',
- title = _title_fmt.format('single')),
- ntypes.longdouble: dict(
- itype = ntypes.longlong,
- fmt = '%s',
- title = _title_fmt.format('long double')),
- ntypes.half: dict(
- itype = ntypes.int16,
- fmt = '%12.5e',
- title = _title_fmt.format('half'))}
- # Key to identify the floating point type. Key is result of
- # ftype('-0.1').newbyteorder('<').tobytes()
- # See:
- # https://perl5.git.perl.org/perl.git/blob/3118d7d684b56cbeb702af874f4326683c45f045:/Configure
- _KNOWN_TYPES = {}
- def _register_type(machar, bytepat):
- _KNOWN_TYPES[bytepat] = machar
- _float_ma = {}
- def _register_known_types():
- # Known parameters for float16
- # See docstring of MachAr class for description of parameters.
- f16 = ntypes.float16
- float16_ma = MachArLike(f16,
- machep=-10,
- negep=-11,
- minexp=-14,
- maxexp=16,
- it=10,
- iexp=5,
- ibeta=2,
- irnd=5,
- ngrd=0,
- eps=exp2(f16(-10)),
- epsneg=exp2(f16(-11)),
- huge=f16(65504),
- tiny=f16(2 ** -14))
- _register_type(float16_ma, b'f\xae')
- _float_ma[16] = float16_ma
- # Known parameters for float32
- f32 = ntypes.float32
- float32_ma = MachArLike(f32,
- machep=-23,
- negep=-24,
- minexp=-126,
- maxexp=128,
- it=23,
- iexp=8,
- ibeta=2,
- irnd=5,
- ngrd=0,
- eps=exp2(f32(-23)),
- epsneg=exp2(f32(-24)),
- huge=f32((1 - 2 ** -24) * 2**128),
- tiny=exp2(f32(-126)))
- _register_type(float32_ma, b'\xcd\xcc\xcc\xbd')
- _float_ma[32] = float32_ma
- # Known parameters for float64
- f64 = ntypes.float64
- epsneg_f64 = 2.0 ** -53.0
- tiny_f64 = 2.0 ** -1022.0
- float64_ma = MachArLike(f64,
- machep=-52,
- negep=-53,
- minexp=-1022,
- maxexp=1024,
- it=52,
- iexp=11,
- ibeta=2,
- irnd=5,
- ngrd=0,
- eps=2.0 ** -52.0,
- epsneg=epsneg_f64,
- huge=(1.0 - epsneg_f64) / tiny_f64 * f64(4),
- tiny=tiny_f64)
- _register_type(float64_ma, b'\x9a\x99\x99\x99\x99\x99\xb9\xbf')
- _float_ma[64] = float64_ma
- # Known parameters for IEEE 754 128-bit binary float
- ld = ntypes.longdouble
- epsneg_f128 = exp2(ld(-113))
- tiny_f128 = exp2(ld(-16382))
- # Ignore runtime error when this is not f128
- with numeric.errstate(all='ignore'):
- huge_f128 = (ld(1) - epsneg_f128) / tiny_f128 * ld(4)
- float128_ma = MachArLike(ld,
- machep=-112,
- negep=-113,
- minexp=-16382,
- maxexp=16384,
- it=112,
- iexp=15,
- ibeta=2,
- irnd=5,
- ngrd=0,
- eps=exp2(ld(-112)),
- epsneg=epsneg_f128,
- huge=huge_f128,
- tiny=tiny_f128)
- # IEEE 754 128-bit binary float
- _register_type(float128_ma,
- b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf')
- _register_type(float128_ma,
- b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf')
- _float_ma[128] = float128_ma
- # Known parameters for float80 (Intel 80-bit extended precision)
- epsneg_f80 = exp2(ld(-64))
- tiny_f80 = exp2(ld(-16382))
- # Ignore runtime error when this is not f80
- with numeric.errstate(all='ignore'):
- huge_f80 = (ld(1) - epsneg_f80) / tiny_f80 * ld(4)
- float80_ma = MachArLike(ld,
- machep=-63,
- negep=-64,
- minexp=-16382,
- maxexp=16384,
- it=63,
- iexp=15,
- ibeta=2,
- irnd=5,
- ngrd=0,
- eps=exp2(ld(-63)),
- epsneg=epsneg_f80,
- huge=huge_f80,
- tiny=tiny_f80)
- # float80, first 10 bytes containing actual storage
- _register_type(float80_ma, b'\xcd\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf')
- _float_ma[80] = float80_ma
- # Guessed / known parameters for double double; see:
- # https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format#Double-double_arithmetic
- # These numbers have the same exponent range as float64, but extended number of
- # digits in the significand.
- huge_dd = nextafter(ld(inf), ld(0), dtype=ld)
- # As the smallest_normal in double double is so hard to calculate we set
- # it to NaN.
- smallest_normal_dd = NaN
- # Leave the same value for the smallest subnormal as double
- smallest_subnormal_dd = ld(nextafter(0., 1.))
- float_dd_ma = MachArLike(ld,
- machep=-105,
- negep=-106,
- minexp=-1022,
- maxexp=1024,
- it=105,
- iexp=11,
- ibeta=2,
- irnd=5,
- ngrd=0,
- eps=exp2(ld(-105)),
- epsneg=exp2(ld(-106)),
- huge=huge_dd,
- tiny=smallest_normal_dd,
- smallest_subnormal=smallest_subnormal_dd)
- # double double; low, high order (e.g. PPC 64)
- _register_type(float_dd_ma,
- b'\x9a\x99\x99\x99\x99\x99Y<\x9a\x99\x99\x99\x99\x99\xb9\xbf')
- # double double; high, low order (e.g. PPC 64 le)
- _register_type(float_dd_ma,
- b'\x9a\x99\x99\x99\x99\x99\xb9\xbf\x9a\x99\x99\x99\x99\x99Y<')
- _float_ma['dd'] = float_dd_ma
- def _get_machar(ftype):
- """ Get MachAr instance or MachAr-like instance
- Get parameters for floating point type, by first trying signatures of
- various known floating point types, then, if none match, attempting to
- identify parameters by analysis.
- Parameters
- ----------
- ftype : class
- Numpy floating point type class (e.g. ``np.float64``)
- Returns
- -------
- ma_like : instance of :class:`MachAr` or :class:`MachArLike`
- Object giving floating point parameters for `ftype`.
- Warns
- -----
- UserWarning
- If the binary signature of the float type is not in the dictionary of
- known float types.
- """
- params = _MACHAR_PARAMS.get(ftype)
- if params is None:
- raise ValueError(repr(ftype))
- # Detect known / suspected types
- key = ftype('-0.1').newbyteorder('<').tobytes()
- ma_like = None
- if ftype == ntypes.longdouble:
- # Could be 80 bit == 10 byte extended precision, where last bytes can
- # be random garbage.
- # Comparing first 10 bytes to pattern first to avoid branching on the
- # random garbage.
- ma_like = _KNOWN_TYPES.get(key[:10])
- if ma_like is None:
- ma_like = _KNOWN_TYPES.get(key)
- if ma_like is not None:
- return ma_like
- # Fall back to parameter discovery
- warnings.warn(
- f'Signature {key} for {ftype} does not match any known type: '
- 'falling back to type probe function.\n'
- 'This warnings indicates broken support for the dtype!',
- UserWarning, stacklevel=2)
- return _discovered_machar(ftype)
- def _discovered_machar(ftype):
- """ Create MachAr instance with found information on float types
- """
- params = _MACHAR_PARAMS[ftype]
- return MachAr(lambda v: array([v], ftype),
- lambda v:_fr0(v.astype(params['itype']))[0],
- lambda v:array(_fr0(v)[0], ftype),
- lambda v: params['fmt'] % array(_fr0(v)[0], ftype),
- params['title'])
- @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.
- machar : MachAr
- The object which calculated these parameters and holds more
- detailed information.
- .. deprecated:: 1.22
- 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.
- 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.
- 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 bits in the mantissa.
- 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
- --------
- MachAr : The implementation of the tests that produce this information.
- 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]_.
- 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, http://www.doi.org/10.1109/IEEESTD.2008.4610935
- .. [2] Wikipedia, "Denormal Numbers",
- https://en.wikipedia.org/wiki/Denormal_number
- Examples
- --------
- >>> np.finfo(np.float64).dtype
- dtype('float64')
- >>> np.finfo(np.complex64).dtype
- dtype('float32')
- """
- _finfo_cache = {}
- def __new__(cls, dtype):
- 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, None)
- if obj is not None:
- return obj
- dtypes = [dtype]
- newdtype = numeric.obj2sctype(dtype)
- if newdtype is not dtype:
- dtypes.append(newdtype)
- dtype = newdtype
- if not issubclass(dtype, numeric.inexact):
- raise ValueError("data type %r not inexact" % (dtype))
- obj = cls._finfo_cache.get(dtype, None)
- if obj is not None:
- return obj
- if not issubclass(dtype, numeric.floating):
- newdtype = _convert_to_float[dtype]
- if newdtype is not dtype:
- dtypes.append(newdtype)
- dtype = newdtype
- obj = cls._finfo_cache.get(dtype, None)
- if obj is not None:
- 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)
- machar = _get_machar(dtype)
- for word in ['precision', 'iexp',
- 'maxexp', 'minexp', 'negep',
- 'machep']:
- setattr(self, word, getattr(machar, word))
- for word in ['resolution', 'epsneg', 'smallest_subnormal']:
- setattr(self, word, getattr(machar, word).flat[0])
- self.bits = self.dtype.itemsize * 8
- self.max = machar.huge.flat[0]
- self.min = -self.max
- self.eps = machar.eps.flat[0]
- self.nexp = machar.iexp
- self.nmant = machar.it
- self._machar = machar
- self._str_tiny = machar._str_xmin.strip()
- self._str_max = machar._str_xmax.strip()
- self._str_epsneg = machar._str_epsneg.strip()
- self._str_eps = machar._str_eps.strip()
- self._str_resolution = machar._str_resolution.strip()
- self._str_smallest_normal = machar._str_smallest_normal.strip()
- self._str_smallest_subnormal = machar._str_smallest_subnormal.strip()
- return self
- def __str__(self):
- fmt = (
- 'Machine parameters for %(dtype)s\n'
- '---------------------------------------------------------------\n'
- 'precision = %(precision)3s resolution = %(_str_resolution)s\n'
- 'machep = %(machep)6s eps = %(_str_eps)s\n'
- 'negep = %(negep)6s epsneg = %(_str_epsneg)s\n'
- 'minexp = %(minexp)6s tiny = %(_str_tiny)s\n'
- 'maxexp = %(maxexp)6s max = %(_str_max)s\n'
- 'nexp = %(nexp)6s min = -max\n'
- 'smallest_normal = %(_str_smallest_normal)s '
- 'smallest_subnormal = %(_str_smallest_subnormal)s\n'
- '---------------------------------------------------------------\n'
- )
- return fmt % self.__dict__
- def __repr__(self):
- c = self.__class__.__name__
- d = self.__dict__.copy()
- d['klass'] = c
- return (("%(klass)s(resolution=%(resolution)s, min=-%(_str_max)s,"
- " max=%(_str_max)s, dtype=%(dtype)s)") % d)
- @property
- def smallest_normal(self):
- """Return the value for the smallest normal.
- Returns
- -------
- smallest_normal : float
- Value for the smallest normal.
- Warns
- -----
- UserWarning
- If the calculated value for the smallest normal is requested for
- double-double.
- """
- # This check is necessary because the value for smallest_normal is
- # platform dependent for longdouble types.
- if isnan(self._machar.smallest_normal.flat[0]):
- warnings.warn(
- 'The value of smallest normal is undefined for double double',
- UserWarning, stacklevel=2)
- return self._machar.smallest_normal.flat[0]
- @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
- @property
- def machar(self):
- """The object which calculated these parameters and holds more
- detailed information.
- .. deprecated:: 1.22
- """
- # Deprecated 2021-10-27, NumPy 1.22
- warnings.warn(
- "`finfo.machar` is deprecated (NumPy 1.22)",
- DeprecationWarning, stacklevel=2,
- )
- return self._machar
- @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:
- >>> 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 = {}
- 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("Invalid integer data type %r." % (self.kind,))
- @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)
|