Skip to content

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): string

Parameters ​

NameDescription
valueThe number to format; NaN and infinities give String(value).
maxFractionDigitsThe largest number of decimals. Defaults to 3.
useGroupingWhether 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

Generated from the JSDoc of the sources.