Skip to content

Commit 94445e8

Browse files
fix: round to correct precision when step uses exponential notation (#8290)
* fix: round to correct precision when step uses exponential notation * fix: correct significand precision calculation for exponential steps * refactor: simplify precision calculation for exponents --------- Co-authored-by: Robert Snow <[email protected]>
1 parent 6bdca86 commit 94445e8

File tree

2 files changed

+40
-3
lines changed

2 files changed

+40
-3
lines changed

packages/@react-stately/utils/src/number.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,18 @@ export function clamp(value: number, min: number = -Infinity, max: number = Infi
2020

2121
export function roundToStepPrecision(value: number, step: number): number {
2222
let roundedValue = value;
23+
let precision = 0;
2324
let stepString = step.toString();
24-
let pointIndex = stepString.indexOf('.');
25-
let precision = pointIndex >= 0 ? stepString.length - pointIndex : 0;
25+
// Handle negative exponents in exponential notation (e.g., "1e-7" → precision 8)
26+
let eIndex = stepString.toLowerCase().indexOf('e-');
27+
if (eIndex > 0) {
28+
precision = Math.abs(Math.floor(Math.log10(Math.abs(step)))) + eIndex;
29+
} else {
30+
let pointIndex = stepString.indexOf('.');
31+
if (pointIndex >= 0) {
32+
precision = stepString.length - pointIndex;
33+
}
34+
}
2635
if (precision > 0) {
2736
let pow = Math.pow(10, precision);
2837
roundedValue = Math.round(roundedValue * pow) / pow;

packages/@react-stately/utils/test/number.test.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {clamp, snapValueToStep} from '../src/number';
1+
import {clamp, roundToStepPrecision, snapValueToStep} from '../src/number';
22

33
describe('number utils', () => {
44
describe('clamp', () => {
@@ -8,6 +8,34 @@ describe('number utils', () => {
88
});
99
});
1010

11+
describe('roundToStepPrecision', () => {
12+
it('should return the input unchanged for integer steps', () => {
13+
expect(roundToStepPrecision(7.123, 1)).toBe(7.123);
14+
expect(roundToStepPrecision(5, 10)).toBe(5);
15+
});
16+
it('should round to the correct decimal places for steps with decimals', () => {
17+
expect(roundToStepPrecision(1.24, 0.1)).toBe(1.24);
18+
expect(roundToStepPrecision(1.456, 0.01)).toBe(1.456);
19+
expect(roundToStepPrecision(1.2345, 0.015)).toBe(1.2345);
20+
expect(roundToStepPrecision(1.2345, 0.25)).toBe(1.235);
21+
// Should not overcount precision
22+
expect(roundToStepPrecision(2.349, 0.100)).toBe(2.35);
23+
expect(roundToStepPrecision(2.35, 0.100)).toBe(2.35);
24+
// Should handle negative values
25+
expect(roundToStepPrecision(-1.456, 0.01)).toBe(-1.456);
26+
// Should handle zero value
27+
expect(roundToStepPrecision(0, 0.01)).toBe(0);
28+
});
29+
it('should handle rounding for exponential step values', () => {
30+
expect(roundToStepPrecision(0.123456789, 1e-3)).toBe(0.1235);
31+
expect(roundToStepPrecision(0.123456789, 1e-7)).toBe(0.12345679);
32+
expect(roundToStepPrecision(0.123456789, 1.5e-7)).toBe(0.123456789);
33+
expect(roundToStepPrecision(0.123456789, 2.5e-6)).toBe(0.12345679);
34+
// Should handle exponential notation steps regardless of e/E case
35+
expect(roundToStepPrecision(0.123456789, 1E-8)).toBe(0.123456789);
36+
});
37+
});
38+
1139
describe('snapValueToStep', () => {
1240
it('should snap value to nearest step based on min and max', () => {
1341
expect(snapValueToStep(2, -0.5, 100, 3)).toBe(2.5);

0 commit comments

Comments
 (0)