Bug summary:
SymmetricalLogLocator appears to suffer a floating-point precision error in calculating the boundaries of the log regions of a symlog plot, which results in the addition of extra major ticks in the linear region of the plot.
Code reproducing the issue:
import numpy as np
import matplotlib.pyplot as plt # v3.2.2
x = y = np.linspace(-1e5,1e5,100)
plt.plot(x,y)
plt.xscale('symlog', linthreshx=1e3, subsx=[2,3,4,5,6,7,8,9])
(OS=Windows 10, Python=3.8.3, Anaconda=2020.07)
Output (with error highlighted in red):

Discussion:
Major ticks generated by SymmetricalLogLocator sometimes include an extra tick in the linear region:
>>> locator = mpl.ticker.SymmetricalLogLocator(linthresh=1e3, base=10)
>>> locator.tick_values(vmin=0, vmax=1e6)
# array([0.e+00, 1.e+02, 1.e+03, 1.e+04, 1.e+05, 1.e+06]) <-- 1.e+02 is in the linear region
SymmetricalLogLocator calculates the inner (towards zero) boundaries of the log regions via the following base-agnostic logarithm:
lo = np.floor(np.log(linthresh) / np.log(base))
However, when base=10, some power-of-ten thresholds cause floating-point precision problems:
>>> base=10
>>> [np.log(linthresh)/np.log(base) for linthresh in base**np.arange(10)]
# [0.0, 1.0, 2.0, 2.9999999999999996, 4.0, 5.0, 5.999999999999999, 7.0, 8.0, 8.999999999999998]
With the np.floor() calculation, this introduces an additional erroneous innermost major tick.
@dstansby proposed a possible fix to a related issue in #14309, which changes np.floor() to np.ceil(). This looks like it would work with the precision errors I identified above, which all err towards zero, but it would fail with bases that err away from zero, e.g., base=5:
>>> base=5
>>> [np.log(linthresh)/np.log(base) for linthresh in base**np.arange(10)]
# [0.0, 1.0, 2.0, 3.0000000000000004, 4.0, 5.0, 6.000000000000001, 7.0, 8.0, 9.0]
Other possible solutions I can think of: (1) rounding to base within a certain proximity, or (2) adding a special case when base=10 wherein np.log10() is used instead of np.log()/np.log(base).
Bug summary:
SymmetricalLogLocatorappears to suffer a floating-point precision error in calculating the boundaries of the log regions of asymlogplot, which results in the addition of extra major ticks in the linear region of the plot.Code reproducing the issue:
(OS=Windows 10, Python=3.8.3, Anaconda=2020.07)
Output (with error highlighted in red):

Discussion:
Major ticks generated by
SymmetricalLogLocatorsometimes include an extra tick in the linear region:SymmetricalLogLocatorcalculates the inner (towards zero) boundaries of the log regions via the following base-agnostic logarithm:However, when
base=10, some power-of-ten thresholds cause floating-point precision problems:With the
np.floor()calculation, this introduces an additional erroneous innermost major tick.@dstansby proposed a possible fix to a related issue in #14309, which changes
np.floor()tonp.ceil(). This looks like it would work with the precision errors I identified above, which all err towards zero, but it would fail with bases that err away from zero, e.g.,base=5:Other possible solutions I can think of: (1) rounding to
basewithin a certain proximity, or (2) adding a special case whenbase=10whereinnp.log10()is used instead ofnp.log()/np.log(base).