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