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