Round Numbers to Decimal Places Safely

Round finite numbers to a bounded number of decimal places using exponent shifting, then format with fixed digits.

TypescriptData Integrity

Code

snippet
function shiftDecimal(value: number, places: number): number {
  const [coefficient, exponent = "0"] = value.toString().split("e");
  return Number(coefficient + "e" + (Number(exponent) + places));
}

export function roundTo(value: number, decimals = 0): number {
  if (!Number.isFinite(value)) throw new TypeError("value must be a finite number");
  if (!Number.isInteger(decimals) || decimals < 0 || decimals > 15) {
    throw new RangeError("decimals must be an integer between 0 and 15");
  }
  const shifted = shiftDecimal(Math.abs(value), decimals);
  const rounded = Math.round(shifted);
  return Math.sign(value) * shiftDecimal(rounded, -decimals);
}

export function formatFixed(value: number, decimals = 2): string {
  if (!Number.isInteger(decimals) || decimals < 0 || decimals > 15) {
    throw new RangeError("decimals must be an integer between 0 and 15");
  }
  return roundTo(value, decimals).toFixed(decimals);
}