diff options
Diffstat (limited to 'numpy/core/getlimits.py')
-rw-r--r-- | numpy/core/getlimits.py | 72 |
1 files changed, 43 insertions, 29 deletions
diff --git a/numpy/core/getlimits.py b/numpy/core/getlimits.py index 2c0f462cc..da9e1d7f3 100644 --- a/numpy/core/getlimits.py +++ b/numpy/core/getlimits.py @@ -5,8 +5,8 @@ __all__ = ['finfo', 'iinfo'] import warnings +from .._utils import set_module from ._machar import MachAr -from .overrides import set_module from . import numeric from . import numerictypes as ntypes from .numeric import array, inf, NaN @@ -147,8 +147,12 @@ _MACHAR_PARAMS = { # Key to identify the floating point type. Key is result of # ftype('-0.1').newbyteorder('<').tobytes() +# +# 20230201 - use (ftype(-1.0) / ftype(10.0)).newbyteorder('<').tobytes() +# instead because stold may have deficiencies on some platforms. # See: # https://perl5.git.perl.org/perl.git/blob/3118d7d684b56cbeb702af874f4326683c45f045:/Configure + _KNOWN_TYPES = {} def _register_type(machar, bytepat): _KNOWN_TYPES[bytepat] = machar @@ -240,8 +244,6 @@ def _register_known_types(): # IEEE 754 128-bit binary float _register_type(float128_ma, b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf') - _register_type(float128_ma, - b'\x9a\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\x99\xfb\xbf') _float_ma[128] = float128_ma # Known parameters for float80 (Intel 80-bit extended precision) @@ -329,7 +331,9 @@ def _get_machar(ftype): if params is None: raise ValueError(repr(ftype)) # Detect known / suspected types - key = ftype('-0.1').newbyteorder('<').tobytes() + # ftype(-1.0) / ftype(10.0) is better than ftype('-0.1') because stold + # may be deficient + key = (ftype(-1.0) / ftype(10.)).newbyteorder('<').tobytes() ma_like = None if ftype == ntypes.longdouble: # Could be 80 bit == 10 byte extended precision, where last bytes can @@ -338,7 +342,14 @@ def _get_machar(ftype): # random garbage. ma_like = _KNOWN_TYPES.get(key[:10]) if ma_like is None: + # see if the full key is known. ma_like = _KNOWN_TYPES.get(key) + if ma_like is None and len(key) == 16: + # machine limits could be f80 masquerading as np.float128, + # find all keys with length 16 and make new dict, but make the keys + # only 10 bytes long, the last bytes can be random garbage + _kt = {k[:10]: v for k, v in _KNOWN_TYPES.items() if len(k) == 16} + ma_like = _kt.get(key[:10]) if ma_like is not None: return ma_like # Fall back to parameter discovery @@ -352,6 +363,9 @@ def _get_machar(ftype): def _discovered_machar(ftype): """ Create MachAr instance with found information on float types + + TODO: MachAr should be retired completely ideally. We currently only + ever use it system with broken longdouble (valgrind, WSL). """ params = _MACHAR_PARAMS[ftype] return MachAr(lambda v: array([v], ftype), @@ -387,11 +401,6 @@ class finfo: iexp : int The number of bits in the exponent portion of the floating point representation. - machar : MachAr - The object which calculated these parameters and holds more - detailed information. - - .. deprecated:: 1.22 machep : int The exponent that yields `eps`. max : floating point number of the appropriate type @@ -432,7 +441,6 @@ class finfo: See Also -------- - MachAr : The implementation of the tests that produce this information. iinfo : The equivalent for integer data types. spacing : The distance between a value and the nearest adjacent number nextafter : The next floating point value after x1 towards x2 @@ -474,13 +482,26 @@ class finfo: _finfo_cache = {} def __new__(cls, dtype): + obj = cls._finfo_cache.get(dtype) # most common path + if obj is not None: + return obj + + if dtype is None: + # Deprecated in NumPy 1.25, 2023-01-16 + warnings.warn( + "finfo() dtype cannot be None. This behavior will " + "raise an error in the future. (Deprecated in NumPy 1.25)", + DeprecationWarning, + stacklevel=2 + ) + try: dtype = numeric.dtype(dtype) except TypeError: # In case a float instance was given dtype = numeric.dtype(type(dtype)) - obj = cls._finfo_cache.get(dtype, None) + obj = cls._finfo_cache.get(dtype) if obj is not None: return obj dtypes = [dtype] @@ -490,17 +511,24 @@ class finfo: dtype = newdtype if not issubclass(dtype, numeric.inexact): raise ValueError("data type %r not inexact" % (dtype)) - obj = cls._finfo_cache.get(dtype, None) + obj = cls._finfo_cache.get(dtype) if obj is not None: return obj if not issubclass(dtype, numeric.floating): newdtype = _convert_to_float[dtype] if newdtype is not dtype: + # dtype changed, for example from complex128 to float64 dtypes.append(newdtype) dtype = newdtype - obj = cls._finfo_cache.get(dtype, None) - if obj is not None: - return obj + + obj = cls._finfo_cache.get(dtype, None) + if obj is not None: + # the original dtype was not in the cache, but the new + # dtype is in the cache. we add the original dtypes to + # the cache and return the result + for dt in dtypes: + cls._finfo_cache[dt] = obj + return obj obj = object.__new__(cls)._init(dtype) for dt in dtypes: cls._finfo_cache[dt] = obj @@ -595,20 +623,6 @@ class finfo: """ return self.smallest_normal - @property - def machar(self): - """The object which calculated these parameters and holds more - detailed information. - - .. deprecated:: 1.22 - """ - # Deprecated 2021-10-27, NumPy 1.22 - warnings.warn( - "`finfo.machar` is deprecated (NumPy 1.22)", - DeprecationWarning, stacklevel=2, - ) - return self._machar - @set_module('numpy') class iinfo: |