1"""Machine limits for Float32 and Float64 and (long double) if available...

2

3"""

4__all__ = ['finfo', 'iinfo']

5

6 import warnings

7

8 from ._machar import MachAr

9 from .overrides import set_module

10 from . import numeric

11 from . import numerictypes as ntypes

12 from .numeric import array, inf, NaN

13 from .umath import log10, exp2, nextafter, isnan

14

15

16 def _fr0(a):

17 """fix rank-0 --> rank-1"""

18 if a.ndim == 0:

19 a = a.copy()

20 a.shape = (1,)

21 return a

22

23

24 def _fr1(a):

25 """fix rank > 0 --> rank-0"""

26 if a.size == 1:

27 a = a.copy()

28 a.shape = ()

29 return a

30

31

32 class MachArLike:

33 """ Object to simulate MachAr instance """

34 def __init__(self, ftype, *, eps, epsneg, huge, tiny,

35 ibeta, smallest_subnormal=None, **kwargs):

36 self.params = _MACHAR_PARAMS[ftype]

37 self.ftype = ftype

38 self.title = self.params['title']

39 # Parameter types same as for discovered MachAr object.

40 if not smallest_subnormal:

41 self._smallest_subnormal = nextafter(

42 self.ftype(0), self.ftype(1), dtype=self.ftype)

43 else:

44 self._smallest_subnormal = smallest_subnormal

45 self.epsilon = self.eps = self._float_to_float(eps)

46 self.epsneg = self._float_to_float(epsneg)

47 self.xmax = self.huge = self._float_to_float(huge)

48 self.xmin = self._float_to_float(tiny)

49 self.smallest_normal = self.tiny = self._float_to_float(tiny)

50 self.ibeta = self.params['itype'](ibeta)

51 self.__dict__.update(kwargs)

52 self.precision = int(-log10(self.eps))

53 self.resolution = self._float_to_float(

54 self._float_conv(10) ** (-self.precision))

55 self._str_eps = self._float_to_str(self.eps)

56 self._str_epsneg = self._float_to_str(self.epsneg)

57 self._str_xmin = self._float_to_str(self.xmin)

58 self._str_xmax = self._float_to_str(self.xmax)

59 self._str_resolution = self._float_to_str(self.resolution)

60 self._str_smallest_normal = self._float_to_str(self.xmin)

61

62 @property

63 def smallest_subnormal(self):

64 """Return the value for the smallest subnormal.

65

66 Returns

67 -------

68 smallest_subnormal : float

69 value for the smallest subnormal.

70

71 Warns

72 -----

73 UserWarning

74 If the calculated value for the smallest subnormal is zero.

75 """

76 # Check that the calculated value is not zero, in case it raises a

77 # warning.

78 value = self._smallest_subnormal

79 if self.ftype(0) == value:

80 warnings.warn(

81 'The value of the smallest subnormal for {} type '

82 'is zero.'.format(self.ftype), UserWarning, stacklevel=2)

83

84 return self._float_to_float(value)

85

86 @property

87 def _str_smallest_subnormal(self):

88 """Return the string representation of the smallest subnormal."""

89 return self._float_to_str(self.smallest_subnormal)

90

91 def _float_to_float(self, value):

92 """Converts float to float.

93

94 Parameters

95 ----------

96 value : float

97 value to be converted.

98 """

99 return _fr1(self._float_conv(value))

100

101 def _float_conv(self, value):

102 """Converts float to conv.

103

104 Parameters

105 ----------

106 value : float

107 value to be converted.

108 """

109 return array([value], self.ftype)

110

111 def _float_to_str(self, value):

112 """Converts float to str.

113

114 Parameters

115 ----------

116 value : float

117 value to be converted.

118 """

119 return self.params['fmt'] % array(_fr0(value)[0], self.ftype)

120

121

122_convert_to_float = {

123 ntypes.csingle: ntypes.single,

124 ntypes.complex_: ntypes.float_,

125 ntypes.clongfloat: ntypes.longfloat

126 }

127

128# Parameters for creating MachAr / MachAr-like objects

129_title_fmt = 'numpy {} precision floating point number'

130_MACHAR_PARAMS = {

131 ntypes.double: dict(

132 itype = ntypes.int64,

133 fmt = '%24.16e',

134 title = _title_fmt.format('double')),

135 ntypes.single: dict(

136 itype = ntypes.int32,

137 fmt = '%15.7e',

138 title = _title_fmt.format('single')),

139 ntypes.longdouble: dict(

140 itype = ntypes.longlong,

141 fmt = '%s',

142 title = _title_fmt.format('long double')),

143 ntypes.half: dict(

144 itype = ntypes.int16,

145 fmt = '%12.5e',

146 title = _title_fmt.format('half'))}

147

148# Key to identify the floating point type. Key is result of

149# ftype('-0.1').newbyteorder('<').tobytes()

150# See:

151# https://perl5.git.perl.org/perl.git/blob/3118d7d684b56cbeb702af874f4326683c45f045:/Configure

152_KNOWN_TYPES = {}

153 def _register_type(machar, bytepat):

154 _KNOWN_TYPES[bytepat] = machar

155_float_ma = {}

156

157

158 def _register_known_types():

159 # Known parameters for float16

160 # See docstring of MachAr class for description of parameters.

161 f16 = ntypes.float16

162 float16_ma = MachArLike(f16,

163 machep=-10,

164 negep=-11,

165 minexp=-14,

166 maxexp=16,

167 it=10,

168 iexp=5,

169 ibeta=2,

170 irnd=5,

171 ngrd=0,

172 eps=exp2(f16(-10)),

173 epsneg=exp2(f16(-11)),

174 huge=f16(65504),

175 tiny=f16(2 ** -14))

176 _register_type(float16_ma, b'f\xae')

177 _float_ma[16] = float16_ma

178

179 # Known parameters for float32

180 f32 = ntypes.float32

181 float32_ma = MachArLike(f32,

182 machep=-23,

183 negep=-24,

184 minexp=-126,

185 maxexp=128,

186 it=23,

187 iexp=8,

188 ibeta=2,

189 irnd=5,

190 ngrd=0,

191 eps=exp2(f32(-23)),

192 epsneg=exp2(f32(-24)),

193 huge=f32((1 - 2 ** -24) * 2**128),

194 tiny=exp2(f32(-126)))

195 _register_type(float32_ma, b'\xcd\xcc\xcc\xbd')

196 _float_ma[32] = float32_ma

197

198 # Known parameters for float64

199 f64 = ntypes.float64

200 epsneg_f64 = 2.0 ** -53.0

201 tiny_f64 = 2.0 ** -1022.0

202 float64_ma = MachArLike(f64,

203 machep=-52,

204 negep=-53,

205 minexp=-1022,

206 maxexp=1024,

207 it=52,

208 iexp=11,

209 ibeta=2,

210 irnd=5,

211 ngrd=0,

212 eps=2.0 ** -52.0,

213 epsneg=epsneg_f64,

214 huge=(1.0 - epsneg_f64) / tiny_f64 * f64(4),

215 tiny=tiny_f64)

216 _register_type(float64_ma, b'\x9a\x99\x99\x99\x99\x99\xb9\xbf')

217 _float_ma[64] = float64_ma

218

219 # Known parameters for IEEE 754 128-bit binary float

220 ld = ntypes.longdouble

221 epsneg_f128 = exp2(ld(-113))

222 tiny_f128 = exp2(ld(-16382))

223 # Ignore runtime error when this is not f128

224 with numeric.errstate(all='ignore'):

225 huge_f128 = (ld(1) - epsneg_f128) / tiny_f128 * ld(4)

226 float128_ma = MachArLike(ld,

227 machep=-112,

228 negep=-113,

229 minexp=-16382,

230 maxexp=16384,

231 it=112,

232 iexp=15,

233 ibeta=2,

234 irnd=5,

235 ngrd=0,

236 eps=exp2(ld(-112)),

237 epsneg=epsneg_f128,

238 huge=huge_f128,

239 tiny=tiny_f128)

240 # IEEE 754 128-bit binary float

241 _register_type(float128_ma,

242 b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf')

243 _register_type(float128_ma,

244 b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf')

245 _float_ma[128] = float128_ma

246

247 # Known parameters for float80 (Intel 80-bit extended precision)

248 epsneg_f80 = exp2(ld(-64))

249 tiny_f80 = exp2(ld(-16382))

250 # Ignore runtime error when this is not f80

251 with numeric.errstate(all='ignore'):

252 huge_f80 = (ld(1) - epsneg_f80) / tiny_f80 * ld(4)

253 float80_ma = MachArLike(ld,

254 machep=-63,

255 negep=-64,

256 minexp=-16382,

257 maxexp=16384,

258 it=63,

259 iexp=15,

260 ibeta=2,

261 irnd=5,

262 ngrd=0,

263 eps=exp2(ld(-63)),

264 epsneg=epsneg_f80,

265 huge=huge_f80,

266 tiny=tiny_f80)

267 # float80, first 10 bytes containing actual storage

268 _register_type(float80_ma, b'\xcd\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xfb\xbf')

269 _float_ma[80] = float80_ma

270

271 # Guessed / known parameters for double double; see:

272 # https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format#Double-double_arithmetic

273 # These numbers have the same exponent range as float64, but extended number of

274 # digits in the significand.

275 huge_dd = nextafter(ld(inf), ld(0), dtype=ld)

276 # As the smallest_normal in double double is so hard to calculate we set

277 # it to NaN.

278 smallest_normal_dd = NaN

279 # Leave the same value for the smallest subnormal as double

280 smallest_subnormal_dd = ld(nextafter(0., 1.))

281 float_dd_ma = MachArLike(ld,

282 machep=-105,

283 negep=-106,

284 minexp=-1022,

285 maxexp=1024,

286 it=105,

287 iexp=11,

288 ibeta=2,

289 irnd=5,

290 ngrd=0,

291 eps=exp2(ld(-105)),

292 epsneg=exp2(ld(-106)),

293 huge=huge_dd,

294 tiny=smallest_normal_dd,

295 smallest_subnormal=smallest_subnormal_dd)

296 # double double; low, high order (e.g. PPC 64)

297 _register_type(float_dd_ma,

298 b'\x9a\x99\x99\x99\x99\x99Y<\x9a\x99\x99\x99\x99\x99\xb9\xbf')

299 # double double; high, low order (e.g. PPC 64 le)

300 _register_type(float_dd_ma,

301 b'\x9a\x99\x99\x99\x99\x99\xb9\xbf\x9a\x99\x99\x99\x99\x99Y<')

302 _float_ma['dd'] = float_dd_ma

303

304

305 def _get_machar(ftype):

306 """ Get MachAr instance or MachAr-like instance

307

308 Get parameters for floating point type, by first trying signatures of

309 various known floating point types, then, if none match, attempting to

310 identify parameters by analysis.

311

312 Parameters

313 ----------

314 ftype : class

315 Numpy floating point type class (e.g. ``np.float64``)

316

317 Returns

318 -------

319 ma_like : instance of :class:`MachAr` or :class:`MachArLike`

320 Object giving floating point parameters for `ftype`.

321

322 Warns

323 -----

324 UserWarning

325 If the binary signature of the float type is not in the dictionary of

326 known float types.

327 """

328 params = _MACHAR_PARAMS.get(ftype)

329 if params is None:

330 raise ValueError(repr(ftype))

331 # Detect known / suspected types

332 key = ftype('-0.1').newbyteorder('<').tobytes()

333 ma_like = None

334 if ftype == ntypes.longdouble:

335 # Could be 80 bit == 10 byte extended precision, where last bytes can

336 # be random garbage.

337 # Comparing first 10 bytes to pattern first to avoid branching on the

338 # random garbage.

339 ma_like = _KNOWN_TYPES.get(key[:10])

340 if ma_like is None:

341 ma_like = _KNOWN_TYPES.get(key)

342 if ma_like is not None:

343 return ma_like

344 # Fall back to parameter discovery

345 warnings.warn(

346 f'Signature {key} for {ftype} does not match any known type: '

347 'falling back to type probe function.\n'

348 'This warnings indicates broken support for the dtype!',

349 UserWarning, stacklevel=2)

350 return _discovered_machar(ftype)

351

352

353 def _discovered_machar(ftype):

354 """ Create MachAr instance with found information on float types

355 """

356 params = _MACHAR_PARAMS[ftype]

357 return MachAr(lambda v: array([v], ftype),

358 lambda v:_fr0(v.astype(params['itype']))[0],

359 lambda v:array(_fr0(v)[0], ftype),

360 lambda v: params['fmt'] % array(_fr0(v)[0], ftype),

361 params['title'])

362

363

364@set_module('numpy')

365 class finfo:

366 """

367 finfo(dtype)

368

369 Machine limits for floating point types.

370

371 Attributes

372 ----------

373 bits : int

374 The number of bits occupied by the type.

375 dtype : dtype

376 Returns the dtype for which `finfo` returns information. For complex

377 input, the returned dtype is the associated ``float*`` dtype for its

378 real and complex components.

379 eps : float

380 The difference between 1.0 and the next smallest representable float

381 larger than 1.0. For example, for 64-bit binary floats in the IEEE-754

382 standard, ``eps = 2**-52``, approximately 2.22e-16.

383 epsneg : float

384 The difference between 1.0 and the next smallest representable float

385 less than 1.0. For example, for 64-bit binary floats in the IEEE-754

386 standard, ``epsneg = 2**-53``, approximately 1.11e-16.

387 iexp : int

388 The number of bits in the exponent portion of the floating point

389 representation.

390 machar : MachAr

391 The object which calculated these parameters and holds more

392 detailed information.

393

394 .. deprecated:: 1.22

395 machep : int

396 The exponent that yields `eps`.

397 max : floating point number of the appropriate type

398 The largest representable number.

399 maxexp : int

400 The smallest positive power of the base (2) that causes overflow.

401 min : floating point number of the appropriate type

402 The smallest representable number, typically ``-max``.

403 minexp : int

404 The most negative power of the base (2) consistent with there

405 being no leading 0's in the mantissa.

406 negep : int

407 The exponent that yields `epsneg`.

408 nexp : int

409 The number of bits in the exponent including its sign and bias.

410 nmant : int

411 The number of bits in the mantissa.

412 precision : int

413 The approximate number of decimal digits to which this kind of

414 float is precise.

415 resolution : floating point number of the appropriate type

416 The approximate decimal resolution of this type, i.e.,

417 ``10**-precision``.

418 tiny : float

419 An alias for `smallest_normal`, kept for backwards compatibility.

420 smallest_normal : float

421 The smallest positive floating point number with 1 as leading bit in

422 the mantissa following IEEE-754 (see Notes).

423 smallest_subnormal : float

424 The smallest positive floating point number with 0 as leading bit in

425 the mantissa following IEEE-754.

426

427 Parameters

428 ----------

429 dtype : float, dtype, or instance

430 Kind of floating point or complex floating point

431 data-type about which to get information.

432

433 See Also

434 --------

435 MachAr : The implementation of the tests that produce this information.

436 iinfo : The equivalent for integer data types.

437 spacing : The distance between a value and the nearest adjacent number

438 nextafter : The next floating point value after x1 towards x2

439

440 Notes

441 -----

442 For developers of NumPy: do not instantiate this at the module level.

443 The initial calculation of these parameters is expensive and negatively

444 impacts import times. These objects are cached, so calling ``finfo()``

445 repeatedly inside your functions is not a problem.

446

447 Note that ``smallest_normal`` is not actually the smallest positive

448 representable value in a NumPy floating point type. As in the IEEE-754

449 standard [1]_, NumPy floating point types make use of subnormal numbers to

450 fill the gap between 0 and ``smallest_normal``. However, subnormal numbers

451 may have significantly reduced precision [2]_.

452

453 This function can also be used for complex data types as well. If used,

454 the output will be the same as the corresponding real float type

455 (e.g. numpy.finfo(numpy.csingle) is the same as numpy.finfo(numpy.single)).

456 However, the output is true for the real and imaginary components.

457

458 References

459 ----------

460 .. [1] IEEE Standard for Floating-Point Arithmetic, IEEE Std 754-2008,

461 pp.1-70, 2008, http://www.doi.org/10.1109/IEEESTD.2008.4610935

462 .. [2] Wikipedia, "Denormal Numbers",

463 https://en.wikipedia.org/wiki/Denormal_number

464

465 Examples

466 --------

467 >>> np.finfo(np.float64).dtype

468 dtype('float64')

469 >>> np.finfo(np.complex64).dtype

470 dtype('float32')

471

472 """

473

474 _finfo_cache = {}

475

476 def __new__(cls, dtype):

477 try:

478 dtype = numeric.dtype(dtype)

479 except TypeError:

480 # In case a float instance was given

481 dtype = numeric.dtype(type(dtype))

482

483 obj = cls._finfo_cache.get(dtype, None)

484 if obj is not None:

485 return obj

486 dtypes = [dtype]

487 newdtype = numeric.obj2sctype(dtype)

488 if newdtype is not dtype:

489 dtypes.append(newdtype)

490 dtype = newdtype

491 if not issubclass(dtype, numeric.inexact):

492 raise ValueError("data type %r not inexact" % (dtype))

493 obj = cls._finfo_cache.get(dtype, None)

494 if obj is not None:

495 return obj

496 if not issubclass(dtype, numeric.floating):

497 newdtype = _convert_to_float[dtype]

498 if newdtype is not dtype:

499 dtypes.append(newdtype)

500 dtype = newdtype

501 obj = cls._finfo_cache.get(dtype, None)

502 if obj is not None:

503 return obj

504 obj = object.__new__(cls)._init(dtype)

505 for dt in dtypes:

506 cls._finfo_cache[dt] = obj

507 return obj

508

509 def _init(self, dtype):

510 self.dtype = numeric.dtype(dtype)

511 machar = _get_machar(dtype)

512

513 for word in ['precision', 'iexp',

514 'maxexp', 'minexp', 'negep',

515 'machep']:

516 setattr(self, word, getattr(machar, word))

517 for word in ['resolution', 'epsneg', 'smallest_subnormal']:

518 setattr(self, word, getattr(machar, word).flat[0])

519 self.bits = self.dtype.itemsize * 8

520 self.max = machar.huge.flat[0]

521 self.min = -self.max

522 self.eps = machar.eps.flat[0]

523 self.nexp = machar.iexp

524 self.nmant = machar.it

525 self._machar = machar

526 self._str_tiny = machar._str_xmin.strip()

527 self._str_max = machar._str_xmax.strip()

528 self._str_epsneg = machar._str_epsneg.strip()

529 self._str_eps = machar._str_eps.strip()

530 self._str_resolution = machar._str_resolution.strip()

531 self._str_smallest_normal = machar._str_smallest_normal.strip()

532 self._str_smallest_subnormal = machar._str_smallest_subnormal.strip()

533 return self

534

535 def __str__(self):

536 fmt = (

537 'Machine parameters for %(dtype)s\n'

538 '---------------------------------------------------------------\n'

539 'precision = %(precision)3s resolution = %(_str_resolution)s\n'

540 'machep = %(machep)6s eps = %(_str_eps)s\n'

541 'negep = %(negep)6s epsneg = %(_str_epsneg)s\n'

542 'minexp = %(minexp)6s tiny = %(_str_tiny)s\n'

543 'maxexp = %(maxexp)6s max = %(_str_max)s\n'

544 'nexp = %(nexp)6s min = -max\n'

545 'smallest_normal = %(_str_smallest_normal)s '

546 'smallest_subnormal = %(_str_smallest_subnormal)s\n'

547 '---------------------------------------------------------------\n'

548 )

549 return fmt % self.__dict__

550

551 def __repr__(self):

552 c = self.__class__.__name__

553 d = self.__dict__.copy()

554 d['klass'] = c

555 return (("%(klass)s(resolution=%(resolution)s, min=-%(_str_max)s,"

556 " max=%(_str_max)s, dtype=%(dtype)s)") % d)

557

558 @property

559 def smallest_normal(self):

560 """Return the value for the smallest normal.

561

562 Returns

563 -------

564 smallest_normal : float

565 Value for the smallest normal.

566

567 Warns

568 -----

569 UserWarning

570 If the calculated value for the smallest normal is requested for

571 double-double.

572 """

573 # This check is necessary because the value for smallest_normal is

574 # platform dependent for longdouble types.

575 if isnan(self._machar.smallest_normal.flat[0]):

576 warnings.warn(

577 'The value of smallest normal is undefined for double double',

578 UserWarning, stacklevel=2)

579 return self._machar.smallest_normal.flat[0]

580

581 @property

582 def tiny(self):

583 """Return the value for tiny, alias of smallest_normal.

584

585 Returns

586 -------

587 tiny : float

588 Value for the smallest normal, alias of smallest_normal.

589

590 Warns

591 -----

592 UserWarning

593 If the calculated value for the smallest normal is requested for

594 double-double.

595 """

596 return self.smallest_normal

597

598 @property

599 def machar(self):

600 """The object which calculated these parameters and holds more

601 detailed information.

602

603 .. deprecated:: 1.22

604 """

605 # Deprecated 2021-10-27, NumPy 1.22

606 warnings.warn(

607 "`finfo.machar` is deprecated (NumPy 1.22)",

608 DeprecationWarning, stacklevel=2,

609 )

610 return self._machar

611

612

613@set_module('numpy')

614 class iinfo:

615 """

616 iinfo(type)

617

618 Machine limits for integer types.

619

620 Attributes

621 ----------

622 bits : int

623 The number of bits occupied by the type.

624 dtype : dtype

625 Returns the dtype for which `iinfo` returns information.

626 min : int

627 The smallest integer expressible by the type.

628 max : int

629 The largest integer expressible by the type.

630

631 Parameters

632 ----------

633 int_type : integer type, dtype, or instance

634 The kind of integer data type to get information about.

635

636 See Also

637 --------

638 finfo : The equivalent for floating point data types.

639

640 Examples

641 --------

642 With types:

643

644 >>> ii16 = np.iinfo(np.int16)

645 >>> ii16.min

646 -32768

647 >>> ii16.max

648 32767

649 >>> ii32 = np.iinfo(np.int32)

650 >>> ii32.min

651 -2147483648

652 >>> ii32.max

653 2147483647

654

655 With instances:

656

657 >>> ii32 = np.iinfo(np.int32(10))

658 >>> ii32.min

659 -2147483648

660 >>> ii32.max

661 2147483647

662

663 """

664

665 _min_vals = {}

666 _max_vals = {}

667

668 def __init__(self, int_type):

669 try:

670 self.dtype = numeric.dtype(int_type)

671 except TypeError:

672 self.dtype = numeric.dtype(type(int_type))

673 self.kind = self.dtype.kind

674 self.bits = self.dtype.itemsize * 8

675 self.key = "%s%d" % (self.kind, self.bits)

676 if self.kind not in 'iu':

677 raise ValueError("Invalid integer data type %r." % (self.kind,))

678

679 @property

680 def min(self):

681 """Minimum value of given dtype."""

682 if self.kind == 'u':

683 return 0

684 else:

685 try:

686 val = iinfo._min_vals[self.key]

687 except KeyError:

688 val = int(-(1 << (self.bits-1)))

689 iinfo._min_vals[self.key] = val

690 return val

691

692 @property

693 def max(self):

694 """Maximum value of given dtype."""

695 try:

696 val = iinfo._max_vals[self.key]

697 except KeyError:

698 if self.kind == 'u':

699 val = int((1 << self.bits) - 1)

700 else:

701 val = int((1 << (self.bits-1)) - 1)

702 iinfo._max_vals[self.key] = val

703 return val

704

705 def __str__(self):

706 """String representation."""

707 fmt = (

708 'Machine parameters for %(dtype)s\n'

709 '---------------------------------------------------------------\n'

710 'min = %(min)s\n'

711 'max = %(max)s\n'

712 '---------------------------------------------------------------\n'

713 )

714 return fmt % {'dtype': self.dtype, 'min': self.min, 'max': self.max}

715

716 def __repr__(self):

717 return "%s(min=%s, max=%s, dtype=%s)" % (self.__class__.__name__,

718 self.min, self.max, self.dtype)