Repository: ant-design/ant-design-mobile (ts)

Issue:
[slider]当step为小数时，某些点位的value精确度异常
### Version of antd-mobile

5.24.0

### Operating system and its version

_No response_

### Browser and its version

Google Chrome 版本 106.0.5249.61（正式版本） (arm64)

### Sandbox to reproduce

https://codesandbox.io/s/boring-lichterman-f8dp3s?file=/src/App.tsx

### What happened?

## 复现步骤
拖动滑块，观察下行的 `value`，会有类似 `9.799999999999997` 的值

## 期望
期望 `value` 与 `step` 值的精确度相同，且是 `step` 值的整数倍。

例如 demo 中的 `step` 值为 `0.2`， 期望出现的值是 `9.8` 而不是 `9.799999999999997`。

Pull request:
fix(Slider): value should be formatted when step is a decimal
Close #5704 

Patch:
diff --git a/src/components/slider/slider.tsx b/src/components/slider/slider.tsx
index 9271b82ee..81349eff4 100644
--- a/src/components/slider/slider.tsx
+++ b/src/components/slider/slider.tsx
@@ -8,6 +8,7 @@ import { mergeProps } from '../../utils/with-default-props'
 import { nearest } from '../../utils/nearest'
 import { usePropsValue } from '../../utils/use-props-value'
 import { devWarning } from '../../utils/dev-log'
+import Big from 'big.js'
 
 const classPrefix = `adm-slider`
 
@@ -52,7 +53,21 @@ export const Slider: FC<SliderProps> = p => {
     return (props.range ? value : [props.min, value]) as any
   }
   function reverseValue(value: [number, number]): SliderValue {
-    return props.range ? value : value[1]
+    const mergedDecimalLen = Math.max(
+      getDecimalLen(step),
+      getDecimalLen(value[0]),
+      getDecimalLen(value[1])
+    )
+    return props.range
+      ? (value.map(v => Big(Big(v).toFixed(mergedDecimalLen)).toNumber()) as [
+          number,
+          number
+        ])
+      : Big(Big(value[1]).toFixed(mergedDecimalLen)).toNumber()
+  }
+
+  function getDecimalLen(n: number) {
+    return (`${n}`.split('.')[1] || '').length
   }
 
   function onAfterChange(value: [number, number]) {
@@ -94,8 +109,8 @@ export const Slider: FC<SliderProps> = p => {
         .sort((a, b) => a - b)
     } else {
       const points: number[] = []
-      for (let i = min; i <= max; i += step) {
-        points.push(i)
+      for (let i = Big(min); i.lt(max); i = i.plus(step)) {
+        points.push(i.toNumber())
       }
       return points
     }


Test patch:
diff --git a/src/components/slider/tests/slider.test.tsx b/src/components/slider/tests/slider.test.tsx
index fdba6059b..80b5dfc89 100644
--- a/src/components/slider/tests/slider.test.tsx
+++ b/src/components/slider/tests/slider.test.tsx
@@ -177,4 +177,47 @@ describe('Slider', () => {
     fireEvent.click(track, { clientX: 60 })
     expect($$(`.${classPrefix}-fill`)[0]).toHaveStyle('width: 60%')
   })
+
+  describe('step is a decimal', () => {
+    beforeAll(() => {
+      Object.defineProperty(HTMLElement.prototype, 'offsetWidth', {
+        value: 90,
+      })
+    })
+
+    test('value should be formatted', () => {
+      const fn = jest.fn()
+      render(<Slider step={0.2} onChange={fn} />)
+      const thumb = screen.getByRole('slider')
+      drag(thumb, 10)
+      expect(fn).toBeCalledWith(11.2)
+    })
+
+    test('value should be formatted when it is a double sliders', () => {
+      const fn = jest.fn()
+      render(<Slider step={0.2} onAfterChange={fn} range />)
+      const thumb1 = screen.getAllByRole('slider')[0]
+      const thumb2 = screen.getAllByRole('slider')[1]
+      drag(thumb2, 80)
+      drag(thumb1, 20)
+      expect(fn).toHaveBeenLastCalledWith([22.2, 88.8])
+    })
+
+    test('float accuracy', () => {
+      const fn = jest.fn()
+      render(<Slider step={1.2} onChange={fn} />)
+      const thumb = screen.getByRole('slider')
+      drag(thumb, 3)
+      // 1.2+2.4 = 3.5999999999999996
+      expect(fn).toBeCalledWith(3.6)
+    })
+
+    test('value is decimal', () => {
+      const fn = jest.fn()
+      render(<Slider min={1.5} max={9.5} onChange={fn} />)
+      const thumb = screen.getByRole('slider')
+      drag(thumb, 30)
+      expect(fn).toBeCalledWith(4.5)
+    })
+  })
 })