diff --git a/src/scales/scale.linearbase.js b/src/scales/scale.linearbase.js index d2da5501eb7..6dece3a5602 100644 --- a/src/scales/scale.linearbase.js +++ b/src/scales/scale.linearbase.js @@ -61,6 +61,9 @@ function generateTicks(generationOptions, dataRange) { if (bounds === 'ticks') { niceMin = Math.floor(rmin / spacing) * spacing; niceMax = Math.ceil(rmax / spacing) * spacing; + const spacingFactor = Math.pow(10, _decimalPlaces(spacing) || 0); + niceMin = Math.round(niceMin * spacingFactor) / spacingFactor; + niceMax = Math.round(niceMax * spacingFactor) / spacingFactor; } else { niceMin = rmin; niceMax = rmax; diff --git a/test/specs/scale.linear.tests.js b/test/specs/scale.linear.tests.js index a8ad53995b1..e0f1bf64dd0 100644 --- a/test/specs/scale.linear.tests.js +++ b/test/specs/scale.linear.tests.js @@ -1395,4 +1395,64 @@ describe('Linear Scale', function() { expect(createChart).not.toThrow(); }); + + it('should not carry floating point drift when bounds are non-round numbers (issue #12281)', function() { + var chart = window.acquireChart({ + type: 'line', + data: { + datasets: [{ + data: [] + }] + }, + options: { + scales: { + y: { + type: 'linear', + min: 49.894, + max: 51.5264, + ticks: { + stepSize: 0.2 + } + } + } + } + }); + + var scale = chart.scales.y; + var values = scale.ticks.map(t => t.value); + expect(values).toEqual([ + 49.894, 50, 50.2, 50.4, 50.6, 50.8, 51, 51.2, 51.4, 51.5264 + ]); + expect(values.filter(v => Number.isInteger(v))).toEqual([50, 51]); + }); + + it('should not carry floating point drift for negative non-round bounds (issue #12281)', function() { + var chart = window.acquireChart({ + type: 'line', + data: { + datasets: [{ + data: [] + }] + }, + options: { + scales: { + y: { + type: 'linear', + min: -51.5264, + max: -49.894, + ticks: { + stepSize: 0.2 + } + } + } + } + }); + + var scale = chart.scales.y; + var values = scale.ticks.map(t => t.value); + expect(values).toEqual([ + -51.5264, -51.4, -51.2, -51, -50.8, -50.6, -50.4, -50.2, -50, -49.894 + ]); + expect(values.filter(v => Number.isInteger(v))).toEqual([-51, -50]); + }); });