Repository: arco-design/arco-design (ts)

Issue:
InputNumber precision, use Math.round to replace toFixed()
- [ ] I'm sure this does not appear in [the issue list of the repository](https://github.com/arco-design/arco-design/issues)

## 基本信息 
- **依赖包名 及 版本:** @arco-design/web-react@2.32.0
- **浏览器版本:** chrome99.0.4844.84
- **复现地址:** N/A

## 预期结果
`value = 0.15` and `precision = 1`, the displayed value should be `0.2`.


<!-- generated by arco-issue. DO NOT REMOVE -->

Pull request:
enhancement: InputNumber with precision use Math.round to replace number.toFixed
## Types of changes

<!-- What types of changes does this PR introduce -->
<!-- Only support choose one type, if there are multiple types, you can add the `Type` column in the Changelog. -->

- [ ] New feature
- [ ] Bug fix
- [x] Enhancement
- [ ] Documentation change
- [ ] Coding style change
- [ ] Component style change
- [ ] Refactoring
- [ ] Test cases
- [ ] Continuous integration
- [ ] Typescript definition change
- [ ] Breaking change
- [ ] Others 

## How is the change tested?

<!-- Unit tests should be added/updated for bug fixes and new features, if applicable -->
<!-- Please describe how you tested the change. E.g. Creating/updating unit tests or attaching a screenshot of how it works with your change -->

## Changelog

| Component | Changelog(CN) | Changelog(EN) | Related issues |
| --------- | ------------- | ------------- | -------------- |
|   InputNumber   |   `InputNumber` 使用 `Math.round` 替换 `Number.prototype.toFixed` 计算小数点后保留的数字，避免末尾数字为 5 时可能出现的临界情况。     | `InputNumber` uses `Math.round` instead of `Number.prototype.toFixed` to calculate the number after the decimal point, to avoid the critical situation that may occur when the final number is 5.  |     Close #782           |

<!-- If there are multiple types, you can add the `Type` column in the Changelog, the value of the column is the same as `Types of changes` -->
## Checklist:

- [x] Test suite passes (`npm run test`)
- [x] Provide changelog for relevant changes (e.g. bug fixes and new features) if applicable.
- [x] Changes are submitted to the appropriate branch (e.g. features should be submitted to `feature` branch and others should be submitted to `main` branch)

## Other information

<!-- Please describe what other information that should be taken care of. E.g. describe the impact if introduce a breaking change -->


Patch:
diff --git a/components/Form/store.tsx b/components/Form/store.tsx
index 7cfb94c3..6d6649b2 100644
--- a/components/Form/store.tsx
+++ b/components/Form/store.tsx
@@ -8,23 +8,11 @@ import Control from './control';
 import { FieldError, FormProps, ValidateFieldsErrors, KeyType, FormValidateFn } from './interface';
 import promisify from './promisify';
 
-type DeepPartial<T> = T extends
-  | string
-  | number
-  | bigint
-  | boolean
-  | null
-  | undefined
-  | symbol
-  | Date
-  ? T | undefined
-  : T extends Array<infer U>
-  ? Array<DeepPartial<U>>
-  : T extends ReadonlyArray<infer U>
-  ? ReadonlyArray<DeepPartial<U>>
-  : {
-      [K in keyof T]?: DeepPartial<T[K]>;
-    };
+export type DeepPartial<T> = T extends object
+  ? {
+      [P in keyof T]?: DeepPartial<T[P]>;
+    }
+  : T;
 
 /**
  * setFieldValue: setFieldsValue, setFieldValue, setFields
@@ -204,8 +192,8 @@ class Store<
     if (!field) return;
     const prev = cloneDeep(this.store);
     set(this.store, field, value);
-    this.triggerValuesChange({ [field]: value } as unknown as Partial<FormData>);
-    this.triggerTouchChange({ [field]: value } as unknown as Partial<FormData>);
+    this.triggerValuesChange(({ [field]: value } as unknown) as Partial<FormData>);
+    this.triggerTouchChange(({ [field]: value } as unknown) as Partial<FormData>);
 
     this.notify('innerSetValue', { prev, field, ...options, changeValues: { [field]: value } });
   };
@@ -252,14 +240,16 @@ class Store<
   };
 
   // 外部调用，设置多个表单控件的值，以及 error，touch 信息。
-  public setFields = (obj: {
-    [field in FieldKey]?: {
-      value?: FieldValue;
-      error?: FieldError<FieldValue>;
-      touched?: boolean;
-      warning?: React.ReactNode;
-    };
-  }) => {
+  public setFields = (
+    obj: {
+      [field in FieldKey]?: {
+        value?: FieldValue;
+        error?: FieldError<FieldValue>;
+        touched?: boolean;
+        warning?: React.ReactNode;
+      };
+    }
+  ) => {
     const fields = Object.keys(obj) as FieldKey[];
     const changeValues = {} as any;
     fields.forEach((field) => {
@@ -350,7 +340,7 @@ class Store<
     if (fields && isArray(fields)) {
       const changeValues = {} as any;
       fields.forEach((field) => {
-        set(this.store, field, this.initialValues[field as unknown as keyof FormData]);
+        set(this.store, field, this.initialValues[(field as unknown) as keyof FormData]);
         changeValues[field] = get(this.store, field);
       });
 
diff --git a/components/InputNumber/index.tsx b/components/InputNumber/index.tsx
index 78aa302f..1293196e 100644
--- a/components/InputNumber/index.tsx
+++ b/components/InputNumber/index.tsx
@@ -20,6 +20,7 @@ import { RefInputType } from '../Input/interface';
 import { InputNumberProps } from './interface';
 import useMergeProps from '../_util/hooks/useMergeProps';
 import omit from '../_util/omit';
+import { toFixed } from './utils';
 
 NP.enableBoundaryChecking(false);
 
@@ -140,7 +141,7 @@ function InputNumber(baseProps: InputNumberProps, ref) {
 
       return isNumber(finalValue)
         ? isNumber(mergedPrecision)
-          ? Number(finalValue.toFixed(mergedPrecision))
+          ? Number(toFixed(finalValue, mergedPrecision))
           : finalValue
         : undefined;
     },
@@ -200,7 +201,7 @@ function InputNumber(baseProps: InputNumberProps, ref) {
     if (isUserInputting) {
       _value = inputValue;
     } else if (isNumber(value) && isNumber(mergedPrecision)) {
-      _value = value.toFixed(mergedPrecision);
+      _value = toFixed(value, mergedPrecision);
     } else if (value == null) {
       _value = '';
     } else {
diff --git a/components/InputNumber/utils.ts b/components/InputNumber/utils.ts
new file mode 100644
index 00000000..9ddd91ec
--- /dev/null
+++ b/components/InputNumber/utils.ts
@@ -0,0 +1,5 @@
+// Replace number.toFixed with Math.round
+export function toFixed(number: number, precision: number): string {
+  const pow = 10 ** precision;
+  return (Math.round(number * pow) / pow).toFixed(precision);
+}


Test patch:
diff --git a/components/InputNumber/__test__/index.test.tsx b/components/InputNumber/__test__/index.test.tsx
index 4d677eb4..a765c578 100644
--- a/components/InputNumber/__test__/index.test.tsx
+++ b/components/InputNumber/__test__/index.test.tsx
@@ -130,10 +130,14 @@ describe('InputNumber ', () => {
   });
 
   it('props precision correctly', () => {
-    const wrapper = mountInputNumber(
-      <InputNumber defaultValue={6} min={0} max={15} step={0.01} precision={3} />
-    );
-    expect(+wrapper.find('input').prop('value')).toBe(6);
+    let wrapper = mountInputNumber(<InputNumber defaultValue={6} step={0.01} precision={3} />);
+    expect(wrapper.find('input').prop('value')).toBe('6.000');
+
+    wrapper = mountInputNumber(<InputNumber defaultValue={6.1234} precision={3} />);
+    expect(wrapper.find('input').prop('value')).toBe('6.123');
+
+    wrapper = mountInputNumber(<InputNumber defaultValue={6.1235} precision={3} />);
+    expect(wrapper.find('input').prop('value')).toBe('6.124');
   });
 
   describe('formatter', () => {