From 45c725f2b283429ba7fecca117cbbb63d04299ed Mon Sep 17 00:00:00 2001
From: Mike Bostock <mbostock@gmail.com>
Date: Sun, 16 Mar 2025 09:02:57 -0700
Subject: [PATCH] fix nice rounding error

---
 src/nice.js       | 13 +++++++++----
 test/nice-test.js | 16 ++++++++++++++++
 2 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/src/nice.js b/src/nice.js
index 579b418c..1ef2de74 100644
--- a/src/nice.js
+++ b/src/nice.js
@@ -7,11 +7,16 @@ export default function nice(start, stop, count) {
     if (step === prestep || step === 0 || !isFinite(step)) {
       return [start, stop];
     } else if (step > 0) {
-      start = Math.floor(start / step) * step;
-      stop = Math.ceil(stop / step) * step;
+      const i = Math.ceil(start / step);
+      const j = Math.floor(stop / step);
+      start = (i - (i * step > start)) * step;
+      stop = (j + (j * step < stop)) * step;
     } else if (step < 0) {
-      start = Math.ceil(start * step) / step;
-      stop = Math.floor(stop * step) / step;
+      const s = -step;
+      const i = Math.ceil(start * s);
+      const j = Math.floor(stop * s);
+      start = (i - (i / s > start)) / s;
+      stop = (j + (j / s < stop)) / s;
     }
     prestep = step;
   }
diff --git a/test/nice-test.js b/test/nice-test.js
index bbc4f708..bbfdf3f2 100644
--- a/test/nice-test.js
+++ b/test/nice-test.js
@@ -44,3 +44,19 @@ it("nice(start, stop, count) returns the expected values", () => {
   assert.deepStrictEqual(nice(132, 876, 5), [0, 1000]);
   assert.deepStrictEqual(nice(132, 876, 1), [0, 1000]);
 });
+
+it("nice(start, stop, count) handles small steps precisely", () => {
+  assert.deepStrictEqual(nice(0.9299999999999999, 1.07, 5000), [0.92998, 1.07]);
+  assert.deepStrictEqual(nice(-1.07, -0.9299999999999999, 5000), [-1.07, -0.92998]);
+  assert.deepStrictEqual(nice(0.929999999999999, 1.07, 5000), [0.92998, 1.07]);
+  assert.deepStrictEqual(nice(-1.07, -0.929999999999999, 5000), [-1.07, -0.92998]);
+  assert.deepStrictEqual(nice(0.92999999999999, 1.07, 5000), [0.92998, 1.07]);
+  assert.deepStrictEqual(nice(-1.07, -0.92999999999999, 5000), [-1.07, -0.92998]);
+  assert.deepStrictEqual(nice(0.930000000000001, 1.07, 5000), [0.93, 1.07]);
+  assert.deepStrictEqual(nice(-1.07, -0.930000000000001, 5000), [-1.07, -0.93]);
+});
+
+it("nice(start, stop, count) handles large steps precisely", () => {
+  assert.deepStrictEqual(nice(0.929999999999999e10, 1.07e10, 5000), [0.92998e10, 1.07e10]);
+  assert.deepStrictEqual(nice(-1.07e10, -0.929999999999999e10, 5000), [-1.07e10, -0.92998e10]);
+});