getlimits.py 18 KB

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