Repository: matplotlib/matplotlib

Issue:
# [Bug]: Colorbar get_ticks() return the incorrect array

### Bug summary

When creating a colorbar using a ScalarMappable with the NoNorm normalization and a discrete colormap (e.g., viridis with a fixed number of colors), the ticks returned by the colorbar’s get_ticks() method do not align with the visually displayed tick positions. This will cause `cbar.set_ticks(cbar.get_ticks())` to change the ticks.

### Code for reproduction

```Python
import matplotlib.pyplot as plt
from matplotlib import cm, colors
data = [1, 2, 3, 4, 5]
fig, ax = plt.subplots()
cbar = fig.colorbar(cm.ScalarMappable(norm=colors.NoNorm(), cmap=plt.get_cmap("viridis", len(data))), ax=ax)
print(cbar.get_ticks())
cbar.set_ticks(cbar.get_ticks()) # this unexpectedly changes the ticks
```

### Actual outcome

[0. 1. 2. 3. 4. 5.]

<img width="510" height="418" alt="Image" src="https://github.com/user-attachments/assets/58104102-4fb1-42f9-8438-2fc5a8bf5c60" />

### Expected outcome

[0. 1. 2. 3. 4.]

<img width="510" height="418" alt="Image" src="https://github.com/user-attachments/assets/065c9d5f-1813-46ff-a687-543d7b45d14c" />

### Additional information

_No response_

### Operating system

_No response_

### Matplotlib Version

3.10.8

### Matplotlib Backend

_No response_

### Python version

_No response_

### Jupyter version

_No response_

### Installation

None

Implementation patch:
diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py
index e27d71974471..83e13841677a 100644
--- a/lib/matplotlib/ticker.py
+++ b/lib/matplotlib/ticker.py
@@ -1767,8 +1767,11 @@ def __call__(self):
         return self.tick_values(dmin, dmax)
 
     def tick_values(self, vmin, vmax):
-        return self.raise_if_exceeds(
-            np.arange(vmin + self.offset, vmax + 1, self._base))
+        # We want tick values in the closed interval [vmin, vmax].
+        # Since np.arange(start, stop) returns values in the semi-open interval
+        # [start, stop), we add a minimal offset so that stop = vmax + eps
+        tick_values = np.arange(vmin + self.offset, vmax + 1e-12, self._base)
+        return self.raise_if_exceeds(tick_values)
 
 
 class FixedLocator(Locator):



Test patch:
diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py
index c3c53ebaea73..478a54b8a317 100644
--- a/lib/matplotlib/tests/test_ticker.py
+++ b/lib/matplotlib/tests/test_ticker.py
@@ -604,6 +604,22 @@ def test_set_params(self):
         assert index._base == 7
         assert index.offset == 7
 
+    def test_tick_values_not_exceeding_vmax(self):
+        """
+        Test that tick_values does not return values greater than vmax.
+        """
+        # Test case where offset=0 could cause vmax to be included incorrectly
+        index = mticker.IndexLocator(base=1, offset=0)
+        assert_array_equal(index.tick_values(0, 4), [0, 1, 2, 3, 4])
+
+        # Test case with fractional offset
+        index = mticker.IndexLocator(base=1, offset=0.5)
+        assert_array_equal(index.tick_values(0, 4), [0.5, 1.5, 2.5, 3.5])
+
+        # Test case with base > 1
+        index = mticker.IndexLocator(base=2, offset=0)
+        assert_array_equal(index.tick_values(0, 5), [0, 2, 4])
+
 
 class TestSymmetricalLogLocator:
     def test_set_params(self):