diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index 0ab02c403d0d..ce08e2470d2f 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -452,6 +452,8 @@ def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, # Always copy, and don't allow array subtypes. A_scaled = np.array(A, dtype=scaled_dtype) + a_min = np.float64(a_min) + a_max = np.float64(a_max) # Clip scaled data around norm if necessary. This is necessary # for big numbers at the edge of float64's ability to represent # changes. Applying a norm first would be good, but ruins the diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index 74742d8c2369..cd7f6c223dd3 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -1238,3 +1238,48 @@ def test_colorbar_format_string_and_old(): plt.imshow([[0, 1]]) cb = plt.colorbar(format="{x}%") assert isinstance(cb._formatter, StrMethodFormatter) + + +def test_colorbar_log_units(): + class FakeQuantity(np.ndarray): + # this is a self-contained version of astropy.units.Quantity + # reduced to a bare minimum to reproduce + # https://github.com/astropy/astropy/issues/11306 + + def __new__(cls, value): + return np.array(value).view(cls) + + def __array_ufunc__(self, function, method, *inputs, **kwargs): + def to_value(q): + value = q.view(np.ndarray) + if value.shape: + return value + else: + return value[()] + + arrays = [to_value(q) for q in inputs] + result = super().__array_ufunc__(function, method, *arrays, **kwargs) + if function not in (np.minimum, np.maximum): + return result + else: + return self._new_view(result) + + def _new_view(self, obj): + obj = np.array(obj, copy=False, subok=True) + view = obj.view(FakeQuantity) + return view + + def __ne__(self, other): + return NotImplemented + + def __float__(self): + raise RuntimeError("boom") + + def item(self, *args): + return self._new_view(super().item(*args)) + + data = FakeQuantity([[1, 2], [3, 4]]) + fig, ax = plt.subplots() + im = ax.imshow(data, norm=LogNorm()) + fig.colorbar(im) + fig.canvas.draw() diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index 5003e2113930..4b0cd1cd035b 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -2853,15 +2853,15 @@ def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): if (not np.isfinite(vmin)) or (not np.isfinite(vmax)): return -expander, expander + # Expand vmin, vmax to float: if they were integer types, they can wrap + # around in abs (abs(np.int8(-128)) == -128) and vmax - vmin can overflow. + vmin, vmax = map(np.float64, (vmin, vmax)) + swapped = False if vmax < vmin: vmin, vmax = vmax, vmin swapped = True - # Expand vmin, vmax to float: if they were integer types, they can wrap - # around in abs (abs(np.int8(-128)) == -128) and vmax - vmin can overflow. - vmin, vmax = map(float, [vmin, vmax]) - maxabsvalue = max(abs(vmin), abs(vmax)) if maxabsvalue < (1e6 / tiny) * np.finfo(float).tiny: vmin = -expander