From f0c908f97aad47ce358739070a108f376082dca2 Mon Sep 17 00:00:00 2001 From: Theeraphat Sorasetsakul Date: Fri, 16 Jun 2023 10:03:36 +0700 Subject: [PATCH 1/6] fix(number-field): fix Floating point Division on validate method --- .../__test__/number-field.validity.test.js | 12 +++++++- packages/elements/src/number-field/index.ts | 30 ++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/elements/src/number-field/__test__/number-field.validity.test.js b/packages/elements/src/number-field/__test__/number-field.validity.test.js index 50d5302874..ef89dcb473 100644 --- a/packages/elements/src/number-field/__test__/number-field.validity.test.js +++ b/packages/elements/src/number-field/__test__/number-field.validity.test.js @@ -1,4 +1,4 @@ -import { fixture, expect } from '@refinitiv-ui/test-helpers'; +import { fixture, expect, elementUpdated } from '@refinitiv-ui/test-helpers'; import '@refinitiv-ui/elements/number-field'; import '@refinitiv-ui/elemental-theme/light/ef-number-field'; @@ -24,3 +24,13 @@ describe('number-field/Validity', () => { }); }); }); + +describe('Check Floating point', function () { + // Test Floating point precision issue that results approximation of real number. e.g. 1111111/0.00001 should equal to 111111100000. + it('Input remains valid upon value update with a step of float value', async function () { + const el = await fixture(''); + el.value="1111111"; + await elementUpdated(el); + expect(el.checkValidity()).to.be.equal(true); + }); +}); \ No newline at end of file diff --git a/packages/elements/src/number-field/index.ts b/packages/elements/src/number-field/index.ts index 073af75281..f83559e897 100644 --- a/packages/elements/src/number-field/index.ts +++ b/packages/elements/src/number-field/index.ts @@ -532,6 +532,29 @@ export class NumberField extends FormFieldElement { return 0; } + /** + * Count precision number + * @param number value to count + * @returns precision number + */ + private getPrecision (number: number): number { + const getDecimalPrecision = (number: string | number): number => { + const [whole, part] = number.toString().split('.'); + return (whole.length ?? 0) + (part?.length ?? 0); + }; + + const numberString = number.toString(); + + // Check if the number is in exponential notation. + if (numberString.includes('e')) { + const [mantissa, exponent] = numberString.split('e'); + const precision = getDecimalPrecision(mantissa) + Math.abs(Number(exponent)); + return precision; + } + + return getDecimalPrecision(numberString); + } + /** * Check if value subtracted from the step base is not an integral multiple of the allowed value step * @param value value to check @@ -542,7 +565,12 @@ export class NumberField extends FormFieldElement { return true; } const decimals = Math.max(this.getDecimalPlace(value), this.stepDecimals); - const division = (this.stepBase - value) / this.getAllowedValueStep(); + const dividend = this.stepBase - value; + const divisor = this.getAllowedValueStep(); + // calculate precision to prevent Floating point precision issue. + // e.g. 1111111/0.00001 would not result in 111111100000 as expected. + const precision = this.getPrecision(dividend) + this.getPrecision(divisor); + const division = parseFloat((dividend / divisor).toPrecision(precision)); const number = decimals ? this.toFixedNumber(division, decimals) : division; // (2 - 1.01) % 0.33 needs to give 0. So we cannot use % directly as it is intended for integers From a88f0f54416620d82bfff63466d5239775809ead Mon Sep 17 00:00:00 2001 From: Theeraphat-Sorasetsakul <86758473+Theeraphat-Sorasetsakul@users.noreply.github.com> Date: Fri, 16 Jun 2023 19:26:02 +0700 Subject: [PATCH 2/6] Update packages/elements/src/number-field/__test__/number-field.validity.test.js Co-authored-by: Wasuwat Limsuparhat <86233706+wsuwt@users.noreply.github.com> --- .../src/number-field/__test__/number-field.validity.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/elements/src/number-field/__test__/number-field.validity.test.js b/packages/elements/src/number-field/__test__/number-field.validity.test.js index ef89dcb473..14e234c4e0 100644 --- a/packages/elements/src/number-field/__test__/number-field.validity.test.js +++ b/packages/elements/src/number-field/__test__/number-field.validity.test.js @@ -28,7 +28,7 @@ describe('number-field/Validity', () => { describe('Check Floating point', function () { // Test Floating point precision issue that results approximation of real number. e.g. 1111111/0.00001 should equal to 111111100000. it('Input remains valid upon value update with a step of float value', async function () { - const el = await fixture(''); + const el = await fixture(''); el.value="1111111"; await elementUpdated(el); expect(el.checkValidity()).to.be.equal(true); From 79492fcffc54304979da384bb1bc101d4d265161 Mon Sep 17 00:00:00 2001 From: Theeraphat-Sorasetsakul <86758473+Theeraphat-Sorasetsakul@users.noreply.github.com> Date: Fri, 16 Jun 2023 19:26:15 +0700 Subject: [PATCH 3/6] Update packages/elements/src/number-field/index.ts Co-authored-by: Wasuwat Limsuparhat <86233706+wsuwt@users.noreply.github.com> --- packages/elements/src/number-field/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/elements/src/number-field/index.ts b/packages/elements/src/number-field/index.ts index f83559e897..7dbcdb4e14 100644 --- a/packages/elements/src/number-field/index.ts +++ b/packages/elements/src/number-field/index.ts @@ -539,7 +539,7 @@ export class NumberField extends FormFieldElement { */ private getPrecision (number: number): number { const getDecimalPrecision = (number: string | number): number => { - const [whole, part] = number.toString().split('.'); + const [wholeNumber, decimalNumber] = number.toString().split('.'); return (whole.length ?? 0) + (part?.length ?? 0); }; From d99bcfd1aa273510ed2ae34c2d3e3c956b147f1d Mon Sep 17 00:00:00 2001 From: Theeraphat Sorasetsakul Date: Fri, 16 Jun 2023 19:39:08 +0700 Subject: [PATCH 4/6] test(number-field): add more Floating point precision test cases --- .../__test__/number-field.validity.test.js | 18 +++++++++++++----- packages/elements/src/number-field/index.ts | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/elements/src/number-field/__test__/number-field.validity.test.js b/packages/elements/src/number-field/__test__/number-field.validity.test.js index 14e234c4e0..ef9697c628 100644 --- a/packages/elements/src/number-field/__test__/number-field.validity.test.js +++ b/packages/elements/src/number-field/__test__/number-field.validity.test.js @@ -27,10 +27,18 @@ describe('number-field/Validity', () => { describe('Check Floating point', function () { // Test Floating point precision issue that results approximation of real number. e.g. 1111111/0.00001 should equal to 111111100000. - it('Input remains valid upon value update with a step of float value', async function () { - const el = await fixture(''); - el.value="1111111"; - await elementUpdated(el); - expect(el.checkValidity()).to.be.equal(true); + describe('Input remains valid upon value update with a step of float value', function () { + it('step = 0.00001 and value = 1111111', async function () { + const el = await fixture(''); + el.value="1111111"; + await elementUpdated(el); + expect(el.checkValidity()).to.be.equal(true); + }); + it('step = 0.14 and value = 7', async function () { + const el = await fixture(''); + el.value="7"; + await elementUpdated(el); + expect(el.checkValidity()).to.be.equal(true); + }); }); }); \ No newline at end of file diff --git a/packages/elements/src/number-field/index.ts b/packages/elements/src/number-field/index.ts index 7dbcdb4e14..d25b71d274 100644 --- a/packages/elements/src/number-field/index.ts +++ b/packages/elements/src/number-field/index.ts @@ -540,7 +540,7 @@ export class NumberField extends FormFieldElement { private getPrecision (number: number): number { const getDecimalPrecision = (number: string | number): number => { const [wholeNumber, decimalNumber] = number.toString().split('.'); - return (whole.length ?? 0) + (part?.length ?? 0); + return (wholeNumber.length ?? 0) + (decimalNumber?.length ?? 0); }; const numberString = number.toString(); From 26112178ff2072828e2f0bcd2d911e23da4085b6 Mon Sep 17 00:00:00 2001 From: Theeraphat-Sorasetsakul <86758473+Theeraphat-Sorasetsakul@users.noreply.github.com> Date: Mon, 19 Jun 2023 11:17:35 +0700 Subject: [PATCH 5/6] Update packages/elements/src/number-field/index.ts Co-authored-by: Wasuwat Limsuparhat <86233706+wsuwt@users.noreply.github.com> --- packages/elements/src/number-field/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/elements/src/number-field/index.ts b/packages/elements/src/number-field/index.ts index d25b71d274..556a238db6 100644 --- a/packages/elements/src/number-field/index.ts +++ b/packages/elements/src/number-field/index.ts @@ -538,7 +538,7 @@ export class NumberField extends FormFieldElement { * @returns precision number */ private getPrecision (number: number): number { - const getDecimalPrecision = (number: string | number): number => { + const getDecimalPrecision = (number: string): number => { const [wholeNumber, decimalNumber] = number.toString().split('.'); return (wholeNumber.length ?? 0) + (decimalNumber?.length ?? 0); }; From d6aa67ad0d0e1f363be0e5d708bcfd3adae67acd Mon Sep 17 00:00:00 2001 From: Theeraphat-Sorasetsakul <86758473+Theeraphat-Sorasetsakul@users.noreply.github.com> Date: Mon, 19 Jun 2023 11:17:45 +0700 Subject: [PATCH 6/6] Update packages/elements/src/number-field/index.ts Co-authored-by: Wasuwat Limsuparhat <86233706+wsuwt@users.noreply.github.com> --- packages/elements/src/number-field/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/elements/src/number-field/index.ts b/packages/elements/src/number-field/index.ts index 556a238db6..234b1c7bec 100644 --- a/packages/elements/src/number-field/index.ts +++ b/packages/elements/src/number-field/index.ts @@ -539,7 +539,7 @@ export class NumberField extends FormFieldElement { */ private getPrecision (number: number): number { const getDecimalPrecision = (number: string): number => { - const [wholeNumber, decimalNumber] = number.toString().split('.'); + const [wholeNumber, decimalNumber] = number.split('.'); return (wholeNumber.length ?? 0) + (decimalNumber?.length ?? 0); };