1"""Machine limits for Float32 and Float64 and (long double) if available...
2
3"""
4__all__ = ['finfo', 'iinfo']
5
6import math
7import types
8import warnings
9from functools import cached_property
10
11from numpy._utils import set_module
12
13from . import numeric, numerictypes as ntypes
14from ._multiarray_umath import _populate_finfo_constants
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
32
33_convert_to_float = {
34 ntypes.csingle: ntypes.single,
35 ntypes.complex128: ntypes.float64,
36 ntypes.clongdouble: ntypes.longdouble
37 }
38
39# Parameters for creating MachAr / MachAr-like objects
40_title_fmt = 'numpy {} precision floating point number'
41_MACHAR_PARAMS = {
42 ntypes.double: {
43 'itype': ntypes.int64,
44 'fmt': '%24.16e',
45 'title': _title_fmt.format('double')},
46 ntypes.single: {
47 'itype': ntypes.int32,
48 'fmt': '%15.7e',
49 'title': _title_fmt.format('single')},
50 ntypes.longdouble: {
51 'itype': ntypes.longlong,
52 'fmt': '%s',
53 'title': _title_fmt.format('long double')},
54 ntypes.half: {
55 'itype': ntypes.int16,
56 'fmt': '%12.5e',
57 'title': _title_fmt.format('half')}}
58
59
60@set_module('numpy')
61class finfo:
62 """
63 finfo(dtype)
64
65 Machine limits for floating point types.
66
67 Attributes
68 ----------
69 bits : int
70 The number of bits occupied by the type.
71 dtype : dtype
72 Returns the dtype for which `finfo` returns information. For complex
73 input, the returned dtype is the associated ``float*`` dtype for its
74 real and complex components.
75 eps : float
76 The difference between 1.0 and the next smallest representable float
77 larger than 1.0. For example, for 64-bit binary floats in the IEEE-754
78 standard, ``eps = 2**-52``, approximately 2.22e-16.
79 epsneg : float
80 The difference between 1.0 and the next smallest representable float
81 less than 1.0. For example, for 64-bit binary floats in the IEEE-754
82 standard, ``epsneg = 2**-53``, approximately 1.11e-16.
83 iexp : int
84 The number of bits in the exponent portion of the floating point
85 representation.
86 machep : int
87 The exponent that yields `eps`.
88 max : floating point number of the appropriate type
89 The largest representable number.
90 maxexp : int
91 The smallest positive power of the base (2) that causes overflow.
92 Corresponds to the C standard MAX_EXP.
93 min : floating point number of the appropriate type
94 The smallest representable number, typically ``-max``.
95 minexp : int
96 The most negative power of the base (2) consistent with there
97 being no leading 0's in the mantissa. Corresponds to the C
98 standard MIN_EXP - 1.
99 negep : int
100 The exponent that yields `epsneg`.
101 nexp : int
102 The number of bits in the exponent including its sign and bias.
103 nmant : int
104 The number of explicit bits in the mantissa (excluding the implicit
105 leading bit for normalized numbers).
106 precision : int
107 The approximate number of decimal digits to which this kind of
108 float is precise.
109 resolution : floating point number of the appropriate type
110 The approximate decimal resolution of this type, i.e.,
111 ``10**-precision``.
112 tiny : float
113 An alias for `smallest_normal`, kept for backwards compatibility.
114 smallest_normal : float
115 The smallest positive floating point number with 1 as leading bit in
116 the mantissa following IEEE-754 (see Notes).
117 smallest_subnormal : float
118 The smallest positive floating point number with 0 as leading bit in
119 the mantissa following IEEE-754.
120
121 Parameters
122 ----------
123 dtype : float, dtype, or instance
124 Kind of floating point or complex floating point
125 data-type about which to get information.
126
127 See Also
128 --------
129 iinfo : The equivalent for integer data types.
130 spacing : The distance between a value and the nearest adjacent number
131 nextafter : The next floating point value after x1 towards x2
132
133 Notes
134 -----
135 For developers of NumPy: do not instantiate this at the module level.
136 The initial calculation of these parameters is expensive and negatively
137 impacts import times. These objects are cached, so calling ``finfo()``
138 repeatedly inside your functions is not a problem.
139
140 Note that ``smallest_normal`` is not actually the smallest positive
141 representable value in a NumPy floating point type. As in the IEEE-754
142 standard [1]_, NumPy floating point types make use of subnormal numbers to
143 fill the gap between 0 and ``smallest_normal``. However, subnormal numbers
144 may have significantly reduced precision [2]_.
145
146 For ``longdouble``, the representation varies across platforms. On most
147 platforms it is IEEE 754 binary128 (quad precision) or binary64-extended
148 (80-bit extended precision). On PowerPC systems, it may use the IBM
149 double-double format (a pair of float64 values), which has special
150 characteristics for precision and range.
151
152 This function can also be used for complex data types as well. If used,
153 the output will be the same as the corresponding real float type
154 (e.g. numpy.finfo(numpy.csingle) is the same as numpy.finfo(numpy.single)).
155 However, the output is true for the real and imaginary components.
156
157 References
158 ----------
159 .. [1] IEEE Standard for Floating-Point Arithmetic, IEEE Std 754-2008,
160 pp.1-70, 2008, https://doi.org/10.1109/IEEESTD.2008.4610935
161 .. [2] Wikipedia, "Denormal Numbers",
162 https://en.wikipedia.org/wiki/Denormal_number
163
164 Examples
165 --------
166 >>> import numpy as np
167 >>> np.finfo(np.float64).dtype
168 dtype('float64')
169 >>> np.finfo(np.complex64).dtype
170 dtype('float32')
171
172 """
173
174 _finfo_cache = {}
175
176 __class_getitem__ = classmethod(types.GenericAlias)
177
178 def __new__(cls, dtype):
179 try:
180 obj = cls._finfo_cache.get(dtype) # most common path
181 if obj is not None:
182 return obj
183 except TypeError:
184 pass
185
186 if dtype is None:
187 # Deprecated in NumPy 1.25, 2023-01-16
188 warnings.warn(
189 "finfo() dtype cannot be None. This behavior will "
190 "raise an error in the future. (Deprecated in NumPy 1.25)",
191 DeprecationWarning,
192 stacklevel=2
193 )
194
195 try:
196 dtype = numeric.dtype(dtype)
197 except TypeError:
198 # In case a float instance was given
199 dtype = numeric.dtype(type(dtype))
200
201 obj = cls._finfo_cache.get(dtype)
202 if obj is not None:
203 return obj
204 dtypes = [dtype]
205 newdtype = ntypes.obj2sctype(dtype)
206 if newdtype is not dtype:
207 dtypes.append(newdtype)
208 dtype = newdtype
209 if not issubclass(dtype, numeric.inexact):
210 raise ValueError(f"data type {dtype!r} not inexact")
211 obj = cls._finfo_cache.get(dtype)
212 if obj is not None:
213 return obj
214 if not issubclass(dtype, numeric.floating):
215 newdtype = _convert_to_float[dtype]
216 if newdtype is not dtype:
217 # dtype changed, for example from complex128 to float64
218 dtypes.append(newdtype)
219 dtype = newdtype
220
221 obj = cls._finfo_cache.get(dtype, None)
222 if obj is not None:
223 # the original dtype was not in the cache, but the new
224 # dtype is in the cache. we add the original dtypes to
225 # the cache and return the result
226 for dt in dtypes:
227 cls._finfo_cache[dt] = obj
228 return obj
229 obj = object.__new__(cls)._init(dtype)
230 for dt in dtypes:
231 cls._finfo_cache[dt] = obj
232 return obj
233
234 def _init(self, dtype):
235 self.dtype = numeric.dtype(dtype)
236 self.bits = self.dtype.itemsize * 8
237 self._fmt = None
238 self._repr = None
239 _populate_finfo_constants(self, self.dtype)
240 return self
241
242 @cached_property
243 def epsneg(self):
244 # Assume typical floating point logic. Could also use nextafter.
245 return self.eps / self._radix
246
247 @cached_property
248 def resolution(self):
249 return self.dtype.type(10)**-self.precision
250
251 @cached_property
252 def machep(self):
253 return int(math.log2(self.eps))
254
255 @cached_property
256 def negep(self):
257 return int(math.log2(self.epsneg))
258
259 @cached_property
260 def nexp(self):
261 # considering all ones (inf/nan) and all zeros (subnormal/zero)
262 return math.ceil(math.log2(self.maxexp - self.minexp + 2))
263
264 @cached_property
265 def iexp(self):
266 # Calculate exponent bits from it's range:
267 return math.ceil(math.log2(self.maxexp - self.minexp))
268
269 def __str__(self):
270 if (fmt := getattr(self, "_fmt", None)) is not None:
271 return fmt
272
273 def get_str(name, pad=None):
274 if (val := getattr(self, name, None)) is None:
275 return "<undefined>"
276 if pad is not None:
277 s = str(val).ljust(pad)
278 return str(val)
279
280 precision = get_str("precision", 3)
281 machep = get_str("machep", 6)
282 negep = get_str("negep", 6)
283 minexp = get_str("minexp", 6)
284 maxexp = get_str("maxexp", 6)
285 resolution = get_str("resolution")
286 eps = get_str("eps")
287 epsneg = get_str("epsneg")
288 tiny = get_str("tiny")
289 smallest_normal = get_str("smallest_normal")
290 smallest_subnormal = get_str("smallest_subnormal")
291 nexp = get_str("nexp", 6)
292 max_ = get_str("max")
293 if hasattr(self, "min") and hasattr(self, "max") and -self.min == self.max:
294 min_ = "-max"
295 else:
296 min_ = get_str("min")
297
298 fmt = (
299 f'Machine parameters for {self.dtype}\n'
300 f'---------------------------------------------------------------\n'
301 f'precision = {precision} resolution = {resolution}\n'
302 f'machep = {machep} eps = {eps}\n'
303 f'negep = {negep} epsneg = {epsneg}\n'
304 f'minexp = {minexp} tiny = {tiny}\n'
305 f'maxexp = {maxexp} max = {max_}\n'
306 f'nexp = {nexp} min = {min_}\n'
307 f'smallest_normal = {smallest_normal} '
308 f'smallest_subnormal = {smallest_subnormal}\n'
309 f'---------------------------------------------------------------\n'
310 )
311 self._fmt = fmt
312 return fmt
313
314 def __repr__(self):
315 if (repr_str := getattr(self, "_repr", None)) is not None:
316 return repr_str
317
318 c = self.__class__.__name__
319
320 # Use precision+1 digits in exponential notation
321 fmt_str = _MACHAR_PARAMS.get(self.dtype.type, {}).get('fmt', '%s')
322 if fmt_str != '%s' and hasattr(self, 'max') and hasattr(self, 'min'):
323 max_str = (fmt_str % self.max).strip()
324 min_str = (fmt_str % self.min).strip()
325 else:
326 max_str = str(self.max)
327 min_str = str(self.min)
328
329 resolution_str = str(self.resolution)
330
331 repr_str = (f"{c}(resolution={resolution_str}, min={min_str},"
332 f" max={max_str}, dtype={self.dtype})")
333 self._repr = repr_str
334 return repr_str
335
336 @cached_property
337 def tiny(self):
338 """Return the value for tiny, alias of smallest_normal.
339
340 Returns
341 -------
342 tiny : float
343 Value for the smallest normal, alias of smallest_normal.
344
345 Warns
346 -----
347 UserWarning
348 If the calculated value for the smallest normal is requested for
349 double-double.
350 """
351 return self.smallest_normal
352
353
354@set_module('numpy')
355class iinfo:
356 """
357 iinfo(type)
358
359 Machine limits for integer types.
360
361 Attributes
362 ----------
363 bits : int
364 The number of bits occupied by the type.
365 dtype : dtype
366 Returns the dtype for which `iinfo` returns information.
367 min : int
368 The smallest integer expressible by the type.
369 max : int
370 The largest integer expressible by the type.
371
372 Parameters
373 ----------
374 int_type : integer type, dtype, or instance
375 The kind of integer data type to get information about.
376
377 See Also
378 --------
379 finfo : The equivalent for floating point data types.
380
381 Examples
382 --------
383 With types:
384
385 >>> import numpy as np
386 >>> ii16 = np.iinfo(np.int16)
387 >>> ii16.min
388 -32768
389 >>> ii16.max
390 32767
391 >>> ii32 = np.iinfo(np.int32)
392 >>> ii32.min
393 -2147483648
394 >>> ii32.max
395 2147483647
396
397 With instances:
398
399 >>> ii32 = np.iinfo(np.int32(10))
400 >>> ii32.min
401 -2147483648
402 >>> ii32.max
403 2147483647
404
405 """
406
407 _min_vals = {}
408 _max_vals = {}
409
410 __class_getitem__ = classmethod(types.GenericAlias)
411
412 def __init__(self, int_type):
413 try:
414 self.dtype = numeric.dtype(int_type)
415 except TypeError:
416 self.dtype = numeric.dtype(type(int_type))
417 self.kind = self.dtype.kind
418 self.bits = self.dtype.itemsize * 8
419 self.key = "%s%d" % (self.kind, self.bits)
420 if self.kind not in 'iu':
421 raise ValueError(f"Invalid integer data type {self.kind!r}.")
422
423 @property
424 def min(self):
425 """Minimum value of given dtype."""
426 if self.kind == 'u':
427 return 0
428 else:
429 try:
430 val = iinfo._min_vals[self.key]
431 except KeyError:
432 val = int(-(1 << (self.bits - 1)))
433 iinfo._min_vals[self.key] = val
434 return val
435
436 @property
437 def max(self):
438 """Maximum value of given dtype."""
439 try:
440 val = iinfo._max_vals[self.key]
441 except KeyError:
442 if self.kind == 'u':
443 val = int((1 << self.bits) - 1)
444 else:
445 val = int((1 << (self.bits - 1)) - 1)
446 iinfo._max_vals[self.key] = val
447 return val
448
449 def __str__(self):
450 """String representation."""
451 fmt = (
452 'Machine parameters for %(dtype)s\n'
453 '---------------------------------------------------------------\n'
454 'min = %(min)s\n'
455 'max = %(max)s\n'
456 '---------------------------------------------------------------\n'
457 )
458 return fmt % {'dtype': self.dtype, 'min': self.min, 'max': self.max}
459
460 def __repr__(self):
461 return "%s(min=%s, max=%s, dtype=%s)" % (self.__class__.__name__,
462 self.min, self.max, self.dtype)