Skip to content

Commit

Permalink
fix(number-field): fix Floating point Division on validate method (#757)
Browse files Browse the repository at this point in the history
* fix(number-field): fix Floating point Division on validate method

* Update packages/elements/src/number-field/__test__/number-field.validity.test.js

Co-authored-by: Wasuwat Limsuparhat <[email protected]>

* Update packages/elements/src/number-field/index.ts

Co-authored-by: Wasuwat Limsuparhat <[email protected]>

* test(number-field): add more Floating point precision test cases

* Update packages/elements/src/number-field/index.ts

Co-authored-by: Wasuwat Limsuparhat <[email protected]>

* Update packages/elements/src/number-field/index.ts

Co-authored-by: Wasuwat Limsuparhat <[email protected]>

---------

Co-authored-by: Wasuwat Limsuparhat <[email protected]>
  • Loading branch information
Theeraphat-Sorasetsakul and wsuwt authored Jun 19, 2023
1 parent cd6806b commit a7095ae
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -24,3 +24,21 @@ 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.
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('<ef-number-field step="0.00001"></ef-number-field>');
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('<ef-number-field step="0.14"></ef-number-field>');
el.value="7";
await elementUpdated(el);
expect(el.checkValidity()).to.be.equal(true);
});
});
});
30 changes: 29 additions & 1 deletion packages/elements/src/number-field/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
const [wholeNumber, decimalNumber] = number.split('.');
return (wholeNumber.length ?? 0) + (decimalNumber?.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
Expand All @@ -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
Expand Down

0 comments on commit a7095ae

Please sign in to comment.