diff --git a/packages/client/hmi-client/src/utils/number.ts b/packages/client/hmi-client/src/utils/number.ts new file mode 100644 index 0000000000..ff5ad2a5e7 --- /dev/null +++ b/packages/client/hmi-client/src/utils/number.ts @@ -0,0 +1,70 @@ +/** + * Converts a number string from its exponential form to normal form. + * + * @param {string} num - The number string in exponential form to convert. + * @returns {string} The number in normal form. + */ +export function exponentialToNumber(num: string): string { + return parseFloat(num).toLocaleString('fullwide', { useGrouping: false }); +} + +/** + * Converts a number string to its NIST form. + * + * @param {string} num - The number string to convert. + * @returns {string} The number in NIST form. + */ +export function numberToNist(num: string) { + num = parseFloat(num).toString(); + + // Split the input by decimal point + let [integerPart, decimalPart] = num.split('.'); + + // Format the integer part + integerPart = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ' '); + + if (num.includes('.') && decimalPart) { + decimalPart = decimalPart.replace(/(\d{3})/g, '$1 ').trim(); + } + + // Construct the formatted number + let formattedNumber = integerPart; + if (decimalPart) { + formattedNumber += `.${decimalPart}`; + } + + return formattedNumber; +} + +/** + * Converts a number string from its NIST form to normal form. + * + * @param {string} num - The number string in NIST form to convert. + * @returns {string} The number in normal form. + */ +export function nistToNumber(num: string): string { + // Remove any spaces from the formatted number + let numStr = num.replace(/ /g, ''); + numStr = parseFloat(numStr).toString(); + return numStr; +} + +/** + * Displays a number string in either exponential form or NIST form, depending on its length. + * + * @param {string} num - The number string to display. + * @returns {string} The number in either exponential form or NIST form. + */ +export function displayNumber(num: string): string { + // Remove negative sign if present + let digitString = parseFloat(num).toString(); + if (num.startsWith('-')) { + digitString = digitString.substring(1); + } + + // Remove decimal point if present + digitString = digitString.replace('.', ''); + + if (digitString.length > 6) return parseFloat(num).toExponential(3); + return numberToNist(num); +} diff --git a/packages/client/hmi-client/tests/unit/utils/number.spec.ts b/packages/client/hmi-client/tests/unit/utils/number.spec.ts new file mode 100644 index 0000000000..cdc3f3e6b1 --- /dev/null +++ b/packages/client/hmi-client/tests/unit/utils/number.spec.ts @@ -0,0 +1,126 @@ +import { describe, expect, it } from 'vitest'; +import { exponentialToNumber, numberToNist, nistToNumber, displayNumber } from '@/utils/number'; + +describe('number util tests', () => { + describe('exponentialToNumber', () => { + it('should correctly convert very large positive exponents', () => { + expect(exponentialToNumber('2e+100')).to.equal(`2${'0'.repeat(100)}`); + expect(exponentialToNumber('3.45e+100')).to.equal(`345${'0'.repeat(98)}`); + }); + + it('should correctly convert very large negative exponents', () => { + expect(exponentialToNumber('-2e+100')).to.equal(`-2${'0'.repeat(100)}`); + expect(exponentialToNumber('-3.45e+100')).to.equal(`-345${'0'.repeat(98)}`); + }); + + it('should return the original string for non-exponential numbers', () => { + expect(exponentialToNumber('456')).to.equal('456'); + expect(exponentialToNumber('4.56')).to.equal('4.56'); + }); + + it('should return "NaN" for non-numeric strings', () => { + expect(exponentialToNumber('def')).to.equal('NaN'); + expect(exponentialToNumber('4.56def')).to.equal('4.56'); + }); + + // Additional test cases + it('should correctly convert zero exponent', () => { + expect(exponentialToNumber('2e0')).to.equal('2'); + expect(exponentialToNumber('-2e0')).to.equal('-2'); + }); + + it('should correctly convert one exponent', () => { + expect(exponentialToNumber('2e1')).to.equal('20'); + expect(exponentialToNumber('-2e1')).to.equal('-20'); + }); + }); + + describe('numberToNist', () => { + it('should correctly format numbers without a decimal part', () => { + expect(numberToNist('1')).to.eq('1'); + expect(numberToNist('12')).to.eq('12'); + expect(numberToNist('123')).to.eq('123'); + expect(numberToNist('1234567')).to.eq('1 234 567'); + }); + + it('should correctly format numbers with a decimal part', () => { + expect(numberToNist('1.1')).to.eq('1.1'); + expect(numberToNist('12.12')).to.eq('12.12'); + expect(numberToNist('123.123')).to.eq('123.123'); + expect(numberToNist('1234567.1234567')).to.eq('1 234 567.123 456 7'); + }); + + it('should correctly format negative numbers', () => { + expect(numberToNist('-1')).to.eq('-1'); + expect(numberToNist('-12')).to.eq('-12'); + expect(numberToNist('-123')).to.eq('-123'); + expect(numberToNist('-1234567.1234567')).to.eq('-1 234 567.123 456 7'); + }); + + it('should return "NaN" for non-numeric strings', () => { + expect(numberToNist('abc')).to.eq('NaN'); + expect(numberToNist('1.23abc')).to.eq('1.23'); + }); + }); + + describe('nistToNumber', () => { + it('should correctly convert numbers without a decimal part', () => { + expect(nistToNumber('1')).to.eq('1'); + expect(nistToNumber('12')).to.eq('12'); + expect(nistToNumber('123')).to.eq('123'); + expect(nistToNumber('1 234 567')).to.eq('1234567'); + }); + + it('should correctly convert numbers with a decimal part', () => { + expect(nistToNumber('1.1')).to.eq('1.1'); + expect(nistToNumber('12.12')).to.eq('12.12'); + expect(nistToNumber('123.123')).to.eq('123.123'); + expect(nistToNumber('1 234 567.123 456 7')).to.eq('1234567.1234567'); + }); + + it('should correctly convert negative numbers', () => { + expect(nistToNumber('-1')).to.eq('-1'); + expect(nistToNumber('-12')).to.eq('-12'); + expect(nistToNumber('-123')).to.eq('-123'); + expect(nistToNumber('-1 234 567.123 456 7')).to.eq('-1234567.1234567'); + }); + + it('should return "NaN" for non-numeric strings', () => { + expect(nistToNumber('abc')).to.eq('NaN'); + expect(nistToNumber('1.23abc')).to.eq('1.23'); + }); + + it('should correctly handle multiple spaces between groups', () => { + expect(nistToNumber('1 234.123 4')).to.eq('1234.1234'); + }); + }); + + describe('displayNumber', () => { + it('should correctly format large numbers using scientific notation', () => { + expect(displayNumber('123456789')).to.eq('1.235e+8'); + }); + + it('should correctly format numbers without a decimal part', () => { + expect(displayNumber('123456')).to.eq('123 456'); + }); + + it('should correctly format numbers with a decimal part', () => { + expect(displayNumber('123.456')).to.eq('123.456'); + expect(displayNumber('1234.56')).to.eq('1 234.56'); + expect(displayNumber('12345.678')).to.eq('1.235e+4'); + }); + + it('should correctly format numbers with leading zeros', () => { + expect(displayNumber('0001234.56')).to.eq('1 234.56'); + }); + + it('should correctly format negative numbers', () => { + expect(displayNumber('-1234.56')).to.eq('-1 234.56'); + }); + + it('should return "NaN" for non-numeric strings', () => { + expect(displayNumber('abc')).to.eq('NaN'); + expect(displayNumber('1.23abc')).to.eq('1.23'); + }); + }); +});