getlimits.py 19 KB

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