From 16f9778c6727531b4c06e407648c0c16e1a6ae3c Mon Sep 17 00:00:00 2001
From: Antony Lee
Date: Wed, 6 Jul 2016 12:30:49 -0700
Subject: [PATCH 1/2] Don't convert vmin, vmax to floats.
They may be float128's in which case precision would be lost; this can
result in `Normalize` returning values (barely) outside of `[0, 1]`.
(The cast to `float` was introduced in 28e1d2, referring to bug 2997687
on SF; it may be worth checking what it was about.)
---
lib/matplotlib/colors.py | 5 +----
lib/matplotlib/tests/test_colors.py | 5 +++++
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py
index 511f46d18710..0ffca3879df5 100644
--- a/lib/matplotlib/colors.py
+++ b/lib/matplotlib/colors.py
@@ -918,8 +918,6 @@ def __call__(self, value, clip=None):
elif vmin > vmax:
raise ValueError("minvalue must be less than or equal to maxvalue")
else:
- vmin = float(vmin)
- vmax = float(vmax)
if clip:
mask = np.ma.getmask(result)
result = np.ma.array(np.clip(result.filled(vmax), vmin, vmax),
@@ -938,8 +936,7 @@ def __call__(self, value, clip=None):
def inverse(self, value):
if not self.scaled():
raise ValueError("Not invertible until scaled")
- vmin = float(self.vmin)
- vmax = float(self.vmax)
+ vmin, vmax = self.vmin, self.vmax
if cbook.iterable(value):
val = np.ma.asarray(value)
diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py
index 072959eb8ff7..ee5a32564eee 100644
--- a/lib/matplotlib/tests/test_colors.py
+++ b/lib/matplotlib/tests/test_colors.py
@@ -194,6 +194,11 @@ def test_Normalize():
_scalar_tester(norm, vals)
_mask_tester(norm, vals)
+ # Don't lose precision on longdoubles (float128 on Linux).
+ vals = np.array([1.2345678901, 9.8765432109], dtype=np.longdouble)
+ norm = mcolors.Normalize(vals.min(), vals.max())
+ assert_array_equal(np.asarray(norm(vals)), [0, 1])
+
def test_SymLogNorm():
"""
From ff78a069bc1ad261ce4001d8347934e3995579b0 Mon Sep 17 00:00:00 2001
From: Antony Lee
Date: Thu, 7 Jul 2016 19:13:51 -0700
Subject: [PATCH 2/2] Proper norm'ing of float128 scalars too.
---
lib/matplotlib/colors.py | 23 +++++++----------------
lib/matplotlib/tests/test_colors.py | 7 ++++++-
2 files changed, 13 insertions(+), 17 deletions(-)
diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py
index 0ffca3879df5..73e0c8570794 100644
--- a/lib/matplotlib/colors.py
+++ b/lib/matplotlib/colors.py
@@ -880,22 +880,13 @@ def process_value(value):
Experimental; we may want to add an option to force the
use of float32.
"""
- if cbook.iterable(value):
- is_scalar = False
- result = np.ma.asarray(value)
- if result.dtype.kind == 'f':
- # this is overkill for lists of floats, but required
- # to support pd.Series as input until we can reliable
- # determine if result and value share memory in all cases
- # (list, tuple, deque, ndarray, Series, ...)
- result = result.copy()
- elif result.dtype.itemsize > 2:
- result = result.astype(float)
- else:
- result = result.astype(np.float32)
- else:
- is_scalar = True
- result = np.ma.array([value]).astype(float)
+ is_scalar = not cbook.iterable(value)
+ if is_scalar:
+ value = [value]
+ dtype = np.min_scalar_type(value)
+ dtype = (np.float32 if dtype.itemsize <= 2
+ else np.promote_types(dtype, float))
+ result = np.ma.array(value, dtype=dtype, copy=True)
return result, is_scalar
def __call__(self, value, clip=None):
diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py
index ee5a32564eee..986a5c9ca5cb 100644
--- a/lib/matplotlib/tests/test_colors.py
+++ b/lib/matplotlib/tests/test_colors.py
@@ -194,10 +194,15 @@ def test_Normalize():
_scalar_tester(norm, vals)
_mask_tester(norm, vals)
- # Don't lose precision on longdoubles (float128 on Linux).
+ # Don't lose precision on longdoubles (float128 on Linux):
+ # for array inputs...
vals = np.array([1.2345678901, 9.8765432109], dtype=np.longdouble)
norm = mcolors.Normalize(vals.min(), vals.max())
assert_array_equal(np.asarray(norm(vals)), [0, 1])
+ # and for scalar ones.
+ eps = np.finfo(np.longdouble).resolution
+ norm = plt.Normalize(1, 1 + 100 * eps)
+ assert_equal(norm(1 + 50 * eps), .5)
def test_SymLogNorm():