summaryrefslogtreecommitdiff
path: root/numpy/core/getlimits.py
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/core/getlimits.py')
-rw-r--r--numpy/core/getlimits.py72
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: