From 05a5db0fec2eced55076736f0b9520641b279ad6 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Mon, 27 Jan 2020 16:19:27 +0100 Subject: [PATCH] Cast vmin/vmax to floats before nonsingular-expanding them. Nonsingular-expansion is fundamentally about adding small floats to separate vmin/vmax so casting to float is normal; this avoids running into plain wrong autoscales with ``` im = imshow([[np.int16(-20000), np.int16(20000)]]) colorbar() print(im.norm.vmin, im.norm.vmax) ``` or ``` im = imshow([[np.int16(-32768), np.int16(0)]]) colorbar() print(im.norm.vmin, im.norm.vmax) ``` (The bug is present as far back as 2.2.0.) --- lib/matplotlib/tests/test_colorbar.py | 11 +++++++++++ lib/matplotlib/transforms.py | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/lib/matplotlib/tests/test_colorbar.py b/lib/matplotlib/tests/test_colorbar.py index 7f9b00b5e7c3..3932177565c5 100644 --- a/lib/matplotlib/tests/test_colorbar.py +++ b/lib/matplotlib/tests/test_colorbar.py @@ -570,3 +570,14 @@ def test_colorbar_label(): cbar3 = fig.colorbar(im, orientation='horizontal', label='horizontal cbar') assert cbar3.ax.get_xlabel() == 'horizontal cbar' + + +@pytest.mark.parametrize("clim", [(-20000, 20000), (-32768, 0)]) +def test_colorbar_int(clim): + # Check that we cast to float early enough to not + # overflow ``int16(20000) - int16(-20000)`` or + # run into ``abs(int16(-32768)) == -32768``. + fig, ax = plt.subplots() + im = ax.imshow([[*map(np.int16, clim)]]) + fig.colorbar(im) + assert (im.norm.vmin, im.norm.vmax) == clim diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index ce517c1d00b7..3292340bf99d 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -2791,6 +2791,10 @@ def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): 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