1"""Machine limits for Float32 and Float64 and (long double) if available...
2
3"""
4__all__ = ['finfo', 'iinfo']
5
6import warnings
7
8from .machar import MachAr
9from .overrides import set_module
10from . import numeric
11from . import numerictypes as ntypes
12from .numeric import array, inf
13from .umath import log10, exp2
14from . import umath
15
16
17def _fr0(a):
18    """fix rank-0 --> rank-1"""
19    if a.ndim == 0:
20        a = a.copy()
21        a.shape = (1,)
22    return a
23
24
25def _fr1(a):
26    """fix rank > 0 --> rank-0"""
27    if a.size == 1:
28        a = a.copy()
29        a.shape = ()
30    return a
31
32class MachArLike:
33    """ Object to simulate MachAr instance """
34
35    def __init__(self,
36                 ftype,
37                 *, eps, epsneg, huge, tiny, ibeta, **kwargs):
38        params = _MACHAR_PARAMS[ftype]
39        float_conv = lambda v: array([v], ftype)
40        float_to_float = lambda v : _fr1(float_conv(v))
41        float_to_str = lambda v: (params['fmt'] % array(_fr0(v)[0], ftype))
42
43        self.title = params['title']
44        # Parameter types same as for discovered MachAr object.
45        self.epsilon = self.eps = float_to_float(eps)
46        self.epsneg = float_to_float(epsneg)
47        self.xmax = self.huge = float_to_float(huge)
48        self.xmin = self.tiny = float_to_float(tiny)
49        self.ibeta = params['itype'](ibeta)
50        self.__dict__.update(kwargs)
51        self.precision = int(-log10(self.eps))
52        self.resolution = float_to_float(float_conv(10) ** (-self.precision))
53        self._str_eps = float_to_str(self.eps)
54        self._str_epsneg = float_to_str(self.epsneg)
55        self._str_xmin = float_to_str(self.xmin)
56        self._str_xmax = float_to_str(self.xmax)
57        self._str_resolution = float_to_str(self.resolution)
58
59_convert_to_float = {
60    ntypes.csingle: ntypes.single,
61    ntypes.complex_: ntypes.float_,
62    ntypes.clongfloat: ntypes.longfloat
63    }
64
65# Parameters for creating MachAr / MachAr-like objects
66_title_fmt = 'numpy {} precision floating point number'
67_MACHAR_PARAMS = {
68    ntypes.double: dict(
69        itype = ntypes.int64,
70        fmt = '%24.16e',
71        title = _title_fmt.format('double')),
72    ntypes.single: dict(
73        itype = ntypes.int32,
74        fmt = '%15.7e',
75        title = _title_fmt.format('single')),
76    ntypes.longdouble: dict(
77        itype = ntypes.longlong,
78        fmt = '%s',
79        title = _title_fmt.format('long double')),
80    ntypes.half: dict(
81        itype = ntypes.int16,
82        fmt = '%12.5e',
83        title = _title_fmt.format('half'))}
84
85# Key to identify the floating point type.  Key is result of
86# ftype('-0.1').newbyteorder('<').tobytes()
87# See:
88# https://perl5.git.perl.org/perl.git/blob/3118d7d684b56cbeb702af874f4326683c45f045:/Configure
89_KNOWN_TYPES = {}
90def _register_type(machar, bytepat):
91    _KNOWN_TYPES[bytepat] = machar
92_float_ma = {}
93
94def _register_known_types():
95    # Known parameters for float16
96    # See docstring of MachAr class for description of parameters.
97    f16 = ntypes.float16
98    float16_ma = MachArLike(f16,
99                            machep=-10,
100                            negep=-11,
101                            minexp=-14,
102                            maxexp=16,
103                            it=10,
104                            iexp=5,
105                            ibeta=2,
106                            irnd=5,
107                            ngrd=0,
108                            eps=exp2(f16(-10)),
109                            epsneg=exp2(f16(-11)),
110                            huge=f16(65504),
111                            tiny=f16(2 ** -14))
112    _register_type(float16_ma, b'f\xae')
113    _float_ma[16] = float16_ma
114
115    # Known parameters for float32
116    f32 = ntypes.float32
117    float32_ma = MachArLike(f32,
118                            machep=-23,
119                            negep=-24,
120                            minexp=-126,
121                            maxexp=128,
122                            it=23,
123                            iexp=8,
124                            ibeta=2,
125                            irnd=5,
126                            ngrd=0,
127                            eps=exp2(f32(-23)),
128                            epsneg=exp2(f32(-24)),
129                            huge=f32((1 - 2 ** -24) * 2**128),
130                            tiny=exp2(f32(-126)))
131    _register_type(float32_ma, b'\xcd\xcc\xcc\xbd')
132    _float_ma[32] = float32_ma
133
134    # Known parameters for float64
135    f64 = ntypes.float64
136    epsneg_f64 = 2.0 ** -53.0
137    tiny_f64 = 2.0 ** -1022.0
138    float64_ma = MachArLike(f64,
139                            machep=-52,
140                            negep=-53,
141                            minexp=-1022,
142                            maxexp=1024,
143                            it=52,
144                            iexp=11,
145                            ibeta=2,
146                            irnd=5,
147                            ngrd=0,
148                            eps=2.0 ** -52.0,
149                            epsneg=epsneg_f64,
150                            huge=(1.0 - epsneg_f64) / tiny_f64 * f64(4),
151                            tiny=tiny_f64)
152    _register_type(float64_ma, b'\x9a\x99\x99\x99\x99\x99\xb9\xbf')
153    _float_ma[64] = float64_ma
154
155    # Known parameters for IEEE 754 128-bit binary float
156    ld = ntypes.longdouble
157    epsneg_f128 = exp2(ld(-113))
158    tiny_f128 = exp2(ld(-16382))
159    # Ignore runtime error when this is not f128
160    with numeric.errstate(all='ignore'):
161        huge_f128 = (ld(1) - epsneg_f128) / tiny_f128 * ld(4)
162    float128_ma = MachArLike(ld,
163                             machep=-112,
164                             negep=-113,
165                             minexp=-16382,
166                             maxexp=16384,
167                             it=112,
168                             iexp=15,
169                             ibeta=2,
170                             irnd=5,
171                             ngrd=0,
172                             eps=exp2(ld(-112)),
173                             epsneg=epsneg_f128,
174                             huge=huge_f128,
175                             tiny=tiny_f128)
176    # IEEE 754 128-bit binary float
177    _register_type(float128_ma,
178        b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf')
179    _register_type(float128_ma,
180        b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf')
181    _float_ma[128] = float128_ma
182
183    # Known parameters for float80 (Intel 80-bit extended precision)
184    epsneg_f80 = exp2(ld(-64))
185    tiny_f80 = exp2(ld(-16382))
186    # Ignore runtime error when this is not f80
187    with numeric.errstate(all='ignore'):
188        huge_f80 = (ld(1) - epsneg_f80) / tiny_f80 * ld(4)
189    float80_ma = MachArLike(ld,
190                            machep=-63,
191                            negep=-64,
192                            minexp=-16382,
193                            maxexp=16384,
194                            it=63,
195                            iexp=15,
196                            ibeta=2,
197                            irnd=5,
198                            ngrd=0,
199                            eps=exp2(ld(-63)),
200                            epsneg=epsneg_f80,
201                            huge=huge_f80,
202                            tiny=tiny_f80)
203    # float80, first 10 bytes containing actual storage
204    _register_type(float80_ma, b'\xcd\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf')
205    _float_ma[80] = float80_ma
206
207    # Guessed / known parameters for double double; see:
208    # https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format#Double-double_arithmetic
209    # These numbers have the same exponent range as float64, but extended number of
210    # digits in the significand.
211    huge_dd = (umath.nextafter(ld(inf), ld(0))
212                if hasattr(umath, 'nextafter')  # Missing on some platforms?
213                else float64_ma.huge)
214    float_dd_ma = MachArLike(ld,
215                              machep=-105,
216                              negep=-106,
217                              minexp=-1022,
218                              maxexp=1024,
219                              it=105,
220                              iexp=11,
221                              ibeta=2,
222                              irnd=5,
223                              ngrd=0,
224                              eps=exp2(ld(-105)),
225                              epsneg= exp2(ld(-106)),
226                              huge=huge_dd,
227                              tiny=exp2(ld(-1022)))
228    # double double; low, high order (e.g. PPC 64)
229    _register_type(float_dd_ma,
230        b'\x9a\x99\x99\x99\x99\x99Y<\x9a\x99\x99\x99\x99\x99\xb9\xbf')
231    # double double; high, low order (e.g. PPC 64 le)
232    _register_type(float_dd_ma,
233        b'\x9a\x99\x99\x99\x99\x99\xb9\xbf\x9a\x99\x99\x99\x99\x99Y<')
234    _float_ma['dd'] = float_dd_ma
235
236
237def _get_machar(ftype):
238    """ Get MachAr instance or MachAr-like instance
239
240    Get parameters for floating point type, by first trying signatures of
241    various known floating point types, then, if none match, attempting to
242    identify parameters by analysis.
243
244    Parameters
245    ----------
246    ftype : class
247        Numpy floating point type class (e.g. ``np.float64``)
248
249    Returns
250    -------
251    ma_like : instance of :class:`MachAr` or :class:`MachArLike`
252        Object giving floating point parameters for `ftype`.
253
254    Warns
255    -----
256    UserWarning
257        If the binary signature of the float type is not in the dictionary of
258        known float types.
259    """
260    params = _MACHAR_PARAMS.get(ftype)
261    if params is None:
262        raise ValueError(repr(ftype))
263    # Detect known / suspected types
264    key = ftype('-0.1').newbyteorder('<').tobytes()
265    ma_like = None
266    if ftype == ntypes.longdouble:
267        # Could be 80 bit == 10 byte extended precision, where last bytes can
268        # be random garbage.
269        # Comparing first 10 bytes to pattern first to avoid branching on the
270        # random garbage.
271        ma_like = _KNOWN_TYPES.get(key[:10])
272    if ma_like is None:
273        ma_like = _KNOWN_TYPES.get(key)
274    if ma_like is not None:
275        return ma_like
276    # Fall back to parameter discovery
277    warnings.warn(
278        'Signature {} for {} does not match any known type: '
279        'falling back to type probe function'.format(key, ftype),
280        UserWarning, stacklevel=2)
281    return _discovered_machar(ftype)
282
283
284def _discovered_machar(ftype):
285    """ Create MachAr instance with found information on float types
286    """
287    params = _MACHAR_PARAMS[ftype]
288    return MachAr(lambda v: array([v], ftype),
289                  lambda v:_fr0(v.astype(params['itype']))[0],
290                  lambda v:array(_fr0(v)[0], ftype),
291                  lambda v: params['fmt'] % array(_fr0(v)[0], ftype),
292                  params['title'])
293
294
295@set_module('numpy')
296class finfo:
297    """
298    finfo(dtype)
299
300    Machine limits for floating point types.
301
302    Attributes
303    ----------
304    bits : int
305        The number of bits occupied by the type.
306    eps : float
307        The difference between 1.0 and the next smallest representable float
308        larger than 1.0. For example, for 64-bit binary floats in the IEEE-754
309        standard, ``eps = 2**-52``, approximately 2.22e-16.
310    epsneg : float
311        The difference between 1.0 and the next smallest representable float
312        less than 1.0. For example, for 64-bit binary floats in the IEEE-754
313        standard, ``epsneg = 2**-53``, approximately 1.11e-16.
314    iexp : int
315        The number of bits in the exponent portion of the floating point
316        representation.
317    machar : MachAr
318        The object which calculated these parameters and holds more
319        detailed information.
320    machep : int
321        The exponent that yields `eps`.
322    max : floating point number of the appropriate type
323        The largest representable number.
324    maxexp : int
325        The smallest positive power of the base (2) that causes overflow.
326    min : floating point number of the appropriate type
327        The smallest representable number, typically ``-max``.
328    minexp : int
329        The most negative power of the base (2) consistent with there
330        being no leading 0's in the mantissa.
331    negep : int
332        The exponent that yields `epsneg`.
333    nexp : int
334        The number of bits in the exponent including its sign and bias.
335    nmant : int
336        The number of bits in the mantissa.
337    precision : int
338        The approximate number of decimal digits to which this kind of
339        float is precise.
340    resolution : floating point number of the appropriate type
341        The approximate decimal resolution of this type, i.e.,
342        ``10**-precision``.
343    tiny : float
344        The smallest positive floating point number with full precision
345        (see Notes).
346
347    Parameters
348    ----------
349    dtype : float, dtype, or instance
350        Kind of floating point data-type about which to get information.
351
352    See Also
353    --------
354    MachAr : The implementation of the tests that produce this information.
355    iinfo : The equivalent for integer data types.
356    spacing : The distance between a value and the nearest adjacent number
357    nextafter : The next floating point value after x1 towards x2
358
359    Notes
360    -----
361    For developers of NumPy: do not instantiate this at the module level.
362    The initial calculation of these parameters is expensive and negatively
363    impacts import times.  These objects are cached, so calling ``finfo()``
364    repeatedly inside your functions is not a problem.
365
366    Note that ``tiny`` is not actually the smallest positive representable
367    value in a NumPy floating point type. As in the IEEE-754 standard [1]_,
368    NumPy floating point types make use of subnormal numbers to fill the
369    gap between 0 and ``tiny``. However, subnormal numbers may have
370    significantly reduced precision [2]_.
371
372    References
373    ----------
374    .. [1] IEEE Standard for Floating-Point Arithmetic, IEEE Std 754-2008,
375           pp.1-70, 2008, http://www.doi.org/10.1109/IEEESTD.2008.4610935
376    .. [2] Wikipedia, "Denormal Numbers",
377           https://en.wikipedia.org/wiki/Denormal_number
378    """
379
380    _finfo_cache = {}
381
382    def __new__(cls, dtype):
383        try:
384            dtype = numeric.dtype(dtype)
385        except TypeError:
386            # In case a float instance was given
387            dtype = numeric.dtype(type(dtype))
388
389        obj = cls._finfo_cache.get(dtype, None)
390        if obj is not None:
391            return obj
392        dtypes = [dtype]
393        newdtype = numeric.obj2sctype(dtype)
394        if newdtype is not dtype:
395            dtypes.append(newdtype)
396            dtype = newdtype
397        if not issubclass(dtype, numeric.inexact):
398            raise ValueError("data type %r not inexact" % (dtype))
399        obj = cls._finfo_cache.get(dtype, None)
400        if obj is not None:
401            return obj
402        if not issubclass(dtype, numeric.floating):
403            newdtype = _convert_to_float[dtype]
404            if newdtype is not dtype:
405                dtypes.append(newdtype)
406                dtype = newdtype
407        obj = cls._finfo_cache.get(dtype, None)
408        if obj is not None:
409            return obj
410        obj = object.__new__(cls)._init(dtype)
411        for dt in dtypes:
412            cls._finfo_cache[dt] = obj
413        return obj
414
415    def _init(self, dtype):
416        self.dtype = numeric.dtype(dtype)
417        machar = _get_machar(dtype)
418
419        for word in ['precision', 'iexp',
420                     'maxexp', 'minexp', 'negep',
421                     'machep']:
422            setattr(self, word, getattr(machar, word))
423        for word in ['tiny', 'resolution', 'epsneg']:
424            setattr(self, word, getattr(machar, word).flat[0])
425        self.bits = self.dtype.itemsize * 8
426        self.max = machar.huge.flat[0]
427        self.min = -self.max
428        self.eps = machar.eps.flat[0]
429        self.nexp = machar.iexp
430        self.nmant = machar.it
431        self.machar = machar
432        self._str_tiny = machar._str_xmin.strip()
433        self._str_max = machar._str_xmax.strip()
434        self._str_epsneg = machar._str_epsneg.strip()
435        self._str_eps = machar._str_eps.strip()
436        self._str_resolution = machar._str_resolution.strip()
437        return self
438
439    def __str__(self):
440        fmt = (
441            'Machine parameters for %(dtype)s\n'
442            '---------------------------------------------------------------\n'
443            'precision = %(precision)3s   resolution = %(_str_resolution)s\n'
444            'machep = %(machep)6s   eps =        %(_str_eps)s\n'
445            'negep =  %(negep)6s   epsneg =     %(_str_epsneg)s\n'
446            'minexp = %(minexp)6s   tiny =       %(_str_tiny)s\n'
447            'maxexp = %(maxexp)6s   max =        %(_str_max)s\n'
448            'nexp =   %(nexp)6s   min =        -max\n'
449            '---------------------------------------------------------------\n'
450            )
451        return fmt % self.__dict__
452
453    def __repr__(self):
454        c = self.__class__.__name__
455        d = self.__dict__.copy()
456        d['klass'] = c
457        return (("%(klass)s(resolution=%(resolution)s, min=-%(_str_max)s,"
458                 " max=%(_str_max)s, dtype=%(dtype)s)") % d)
459
460
461@set_module('numpy')
462class iinfo:
463    """
464    iinfo(type)
465
466    Machine limits for integer types.
467
468    Attributes
469    ----------
470    bits : int
471        The number of bits occupied by the type.
472    min : int
473        The smallest integer expressible by the type.
474    max : int
475        The largest integer expressible by the type.
476
477    Parameters
478    ----------
479    int_type : integer type, dtype, or instance
480        The kind of integer data type to get information about.
481
482    See Also
483    --------
484    finfo : The equivalent for floating point data types.
485
486    Examples
487    --------
488    With types:
489
490    >>> ii16 = np.iinfo(np.int16)
491    >>> ii16.min
492    -32768
493    >>> ii16.max
494    32767
495    >>> ii32 = np.iinfo(np.int32)
496    >>> ii32.min
497    -2147483648
498    >>> ii32.max
499    2147483647
500
501    With instances:
502
503    >>> ii32 = np.iinfo(np.int32(10))
504    >>> ii32.min
505    -2147483648
506    >>> ii32.max
507    2147483647
508
509    """
510
511    _min_vals = {}
512    _max_vals = {}
513
514    def __init__(self, int_type):
515        try:
516            self.dtype = numeric.dtype(int_type)
517        except TypeError:
518            self.dtype = numeric.dtype(type(int_type))
519        self.kind = self.dtype.kind
520        self.bits = self.dtype.itemsize * 8
521        self.key = "%s%d" % (self.kind, self.bits)
522        if self.kind not in 'iu':
523            raise ValueError("Invalid integer data type %r." % (self.kind,))
524
525    @property
526    def min(self):
527        """Minimum value of given dtype."""
528        if self.kind == 'u':
529            return 0
530        else:
531            try:
532                val = iinfo._min_vals[self.key]
533            except KeyError:
534                val = int(-(1 << (self.bits-1)))
535                iinfo._min_vals[self.key] = val
536            return val
537
538    @property
539    def max(self):
540        """Maximum value of given dtype."""
541        try:
542            val = iinfo._max_vals[self.key]
543        except KeyError:
544            if self.kind == 'u':
545                val = int((1 << self.bits) - 1)
546            else:
547                val = int((1 << (self.bits-1)) - 1)
548            iinfo._max_vals[self.key] = val
549        return val
550
551    def __str__(self):
552        """String representation."""
553        fmt = (
554            'Machine parameters for %(dtype)s\n'
555            '---------------------------------------------------------------\n'
556            'min = %(min)s\n'
557            'max = %(max)s\n'
558            '---------------------------------------------------------------\n'
559            )
560        return fmt % {'dtype': self.dtype, 'min': self.min, 'max': self.max}
561
562    def __repr__(self):
563        return "%s(min=%s, max=%s, dtype=%s)" % (self.__class__.__name__,
564                                    self.min, self.max, self.dtype)
565