getlimits.py 15 KB

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