diff options
Diffstat (limited to 'numpy/core/getlimits.py')
-rw-r--r-- | numpy/core/getlimits.py | 47 |
1 files changed, 39 insertions, 8 deletions
diff --git a/numpy/core/getlimits.py b/numpy/core/getlimits.py index 5baeb97fe..da9e1d7f3 100644 --- a/numpy/core/getlimits.py +++ b/numpy/core/getlimits.py @@ -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 @@ -471,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] @@ -487,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 |