formatDecimal function
Formats a number with . before the decimals, no trailing zeros, no scientific notation and no -0. Rounds like ICU: formatDecimal(1.005, 2) is '1.01', where toFixed gives '1.00'.
ts
import { formatDecimal } from 'utils';Signature
ts
export function formatDecimal(value: number, maxFractionDigits = 3, useGrouping = false): stringParameters
| Name | Description |
|---|---|
value | The number to format; NaN and infinities give String(value). |
maxFractionDigits | The largest number of decimals. Defaults to 3. |
useGrouping | Whether to separate the thousands with ,. Defaults to false. |
Returns
The formatted number.
Example
ts
formatDecimal(Math.PI); // '3.142'
formatDecimal(2, 2); // '2'
formatDecimal(1234.5, 2, true); // '1,234.5'
formatDecimal(-0.001, 2); // '0'Tests
Line coverage: 100 %.
- ✅ formats 2 with 2 digits as 2
- ✅ formats 1.5 with 3 digits as 1.5
- ✅ formats 1e-7 with 8 digits as 0.0000001
- ✅ formats 1.005 with 2 digits as 1.01
- ✅ formats -0.001 with 2 digits as 0
- ✅ formats -0 with 2 digits as 0
- ✅ formats -1.25 with 1 digits as -1.3
- ✅ formats 1234567.891 with 2 digits as 1234567.89
- ✅ formats 1e+21 with 0 digits as 1000000000000000000000
- ✅ formats 3.141592653589793 with 0 digits as 3
- ✅ formats NaN as NaN
- ✅ formats Infinity as Infinity
- ✅ formats -Infinity as -Infinity
- ✅ accepts the maxFractionDigits bounds
- ✅ groups the thousands with a comma when asked
- ✅ takes 3 decimals and no grouping
Source
src/format/format-decimal.ts (on GitHub)
Source code
ts
const formatters = [new Map<number, Intl.NumberFormat>(), new Map<number, Intl.NumberFormat>()] as const;
/**
* Formats a number with `.` before the decimals, no trailing zeros, no scientific notation and no `-0`.
* Rounds like ICU: `formatDecimal(1.005, 2)` is `'1.01'`, where `toFixed` gives `'1.00'`.
*
* @param value - The number to format; `NaN` and infinities give `String(value)`.
* @param maxFractionDigits - The largest number of decimals. Defaults to `3`.
* @param useGrouping - Whether to separate the thousands with `,`. Defaults to `false`.
* @returns The formatted number.
* @example
* formatDecimal(Math.PI); // '3.142'
* formatDecimal(2, 2); // '2'
* formatDecimal(1234.5, 2, true); // '1,234.5'
* formatDecimal(-0.001, 2); // '0'
*/
export function formatDecimal(value: number, maxFractionDigits = 3, useGrouping = false): string {
if (!Number.isFinite(value)) {
return String(value);
}
const cache = formatters[useGrouping ? 1 : 0];
let formatter = cache.get(maxFractionDigits);
if (!formatter) {
formatter = new Intl.NumberFormat('en-US', {
maximumFractionDigits: maxFractionDigits,
useGrouping,
signDisplay: 'negative',
});
cache.set(maxFractionDigits, formatter);
}
return formatter.format(value);
}Test file
ts
import { formatDecimal } from './format-decimal';
describe(formatDecimal, () => {
it.for([
[2, 2, '2'],
[1.5, 3, '1.5'],
[0.0000001, 8, '0.0000001'],
[1.005, 2, '1.01'],
[-0.001, 2, '0'],
[-0, 2, '0'],
[-1.25, 1, '-1.3'],
[1_234_567.891, 2, '1234567.89'],
[1e21, 0, '1000000000000000000000'],
[Math.PI, 0, '3'],
] as const)('formats %s with %s digits as %s', ([value, digits, expected]) => {
expect(formatDecimal(value, digits)).toBe(expected);
});
it.for([
[NaN, 'NaN'],
[Infinity, 'Infinity'],
[-Infinity, '-Infinity'],
] as const)('formats %s as %s', ([value, expected]) => {
expect(formatDecimal(value, 2)).toBe(expected);
});
it('accepts the maxFractionDigits bounds', () => {
expect(formatDecimal(1.5, 0)).toBe('2');
expect(formatDecimal(1.5, 100)).toBe('1.5');
});
it('groups the thousands with a comma when asked', () => {
expect(formatDecimal(1_234_567.891, 2, true)).toBe('1,234,567.89');
expect(formatDecimal(1234.5, 2, false)).toBe('1234.5');
});
it('takes 3 decimals and no grouping', () => {
expect(formatDecimal(1234.5678)).toBe('1234.568');
});
});Keywords: format, decimal, formatting, function