123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- """Machine limits for Float32 and Float64 and (long double) if available...
- """
- from __future__ import division, absolute_import, print_function
- __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
- from .umath import log10, exp2
- from . import umath
- 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):
- """ Object to simulate MachAr instance """
- def __init__(self,
- ftype,
- **kwargs):
- params = _MACHAR_PARAMS[ftype]
- float_conv = lambda v: array([v], ftype)
- float_to_float = lambda v : _fr1(float_conv(v))
- float_to_str = lambda v: (params['fmt'] % array(_fr0(v)[0], ftype))
- self.title = params['title']
- # Parameter types same as for discovered MachAr object.
- self.epsilon = self.eps = float_to_float(kwargs.pop('eps'))
- self.epsneg = float_to_float(kwargs.pop('epsneg'))
- self.xmax = self.huge = float_to_float(kwargs.pop('huge'))
- self.xmin = self.tiny = float_to_float(kwargs.pop('tiny'))
- self.ibeta = params['itype'](kwargs.pop('ibeta'))
- self.__dict__.update(kwargs)
- self.precision = int(-log10(self.eps))
- self.resolution = float_to_float(float_conv(10) ** (-self.precision))
- self._str_eps = float_to_str(self.eps)
- self._str_epsneg = float_to_str(self.epsneg)
- self._str_xmin = float_to_str(self.xmin)
- self._str_xmax = float_to_str(self.xmax)
- self._str_resolution = float_to_str(self.resolution)
- _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 = (umath.nextafter(ld(inf), ld(0))
- if hasattr(umath, 'nextafter') # Missing on some platforms?
- else float64_ma.huge)
- 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=exp2(ld(-1022)))
- # 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 = _KNOWN_TYPES.get(key)
- # Could be 80 bit == 10 byte extended precision, where last bytes can be
- # random garbage. Try comparing first 10 bytes to pattern.
- if ma_like is None and ftype == ntypes.longdouble:
- ma_like = _KNOWN_TYPES.get(key[:10])
- if ma_like is not None:
- return ma_like
- # Fall back to parameter discovery
- warnings.warn(
- 'Signature {} for {} does not match any known type: '
- 'falling back to type probe function'.format(key, ftype),
- 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(object):
- """
- finfo(dtype)
- Machine limits for floating point types.
- Attributes
- ----------
- bits : int
- The number of bits occupied by the type.
- eps : float
- The smallest representable positive number such that
- ``1.0 + eps != 1.0``. Type of `eps` is an appropriate floating
- point type.
- epsneg : floating point number of the appropriate type
- The smallest representable positive number such that
- ``1.0 - epsneg != 1.0``.
- 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.
- 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
- The smallest positive usable number. Type of `tiny` is an
- appropriate floating point type.
- Parameters
- ----------
- dtype : float, dtype, or instance
- Kind of 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.
- 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.
- """
- _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 ['tiny', 'resolution', 'epsneg']:
- 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()
- 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'
- '---------------------------------------------------------------\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)
- @set_module('numpy')
- class iinfo(object):
- """
- iinfo(type)
- Machine limits for integer types.
- Attributes
- ----------
- bits : int
- The number of bits occupied by the type.
- 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)
|