Functional Weave
Code in TypeScript

text.format-decimal@1.0.0

impl/typescript.ts

1,663 bytes · the TypeScript implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import { roundFloat } from "./math_round_float.ts";  ← from math.round-float ^1.0.0 · built alongside by fune

const POW10 = [1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12];

/**
 * A float as decimal text, never in exponent form and never "-0".
 *
 * String(x), str(x) and Rust's Display disagree about floats: 1e21, 1e-7 and
 * 2.0 print differently in each. So the number is rounded once, turned into a
 * whole count of 10^-decimals units, and the digits of that integer are laid
 * out by hand.
 */
export function formatDecimal(value: number, decimals: number, trimZeros: boolean, groupSeparator: string): string {
  const rounded = roundFloat(value, decimals);
  const scaled = Math.abs(rounded) * POW10[decimals];
  if (scaled >= 1e15) {
    throw new RangeError(`value is too large to format at ${decimals} decimal places, received ${value}`);
  }
  // rounded is within half an ulp of a whole number of units; below 10^15 the
  // product cannot drift as far as the next one, so rounding recovers it.
  const units = roundFloat(scaled, 0);
  let digits = String(units);
  while (digits.length < decimals + 1) digits = "0" + digits;
  const whole = digits.slice(0, digits.length - decimals);
  let fraction = digits.slice(digits.length - decimals);
  if (trimZeros) {
    let end = fraction.length;
    while (end > 0 && fraction[end - 1] === "0") end--;
    fraction = fraction.slice(0, end);
  }
  let grouped = "";
  for (let i = 0; i < whole.length; i++) {
    if (i > 0 && (whole.length - i) % 3 === 0) grouped += groupSeparator;
    grouped += whole[i];
  }
  const body = fraction.length > 0 ? grouped + "." + fraction : grouped;
  return units > 0 && rounded < 0 ? "-" + body : body;
}