Resources Numerical accuracy Calculation policy

Rates, percentages and extended amounts

Reads a typed unit rate and percentage, and works out quantity × rate to the cent.

How the precision is kept

Money is held as whole numbers: a unit rate in 1 ÷ 10000 of a dollar, an amount in cents, waste in basis points. The only floating-point step is quantity × rate, which is rounded to a whole cent at once. Whole numbers below 2⁵³ add exactly in 64-bit.

Calculations under this policy

6 calculations in 2 source files. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.

units.js TakeoffCraft 0.8.1 64-bit 5 calculations Reviewed

Tests bin/test-takeoffcraft.mjs

Typed unit rate

Exact

Reads a rate such as $1,234.5 into whole 1 ÷ 10000 dollars.

rate4 = round(roundTo(v, 4) × 10000)

The text is converted by Number, rounded to 4 decimals by roundTo, multiplied by 10000 and rounded to a whole number. More than 4 typed decimals are rounded away here. The largest accepted rate gives 1e13, well inside the exact integer range.

  • units.js
  • lines 324 to 332
  • TakeoffCraft 0.8.1
/** "$1,234.5" → 12345000 (1/10000 dollar). Up to 4 decimals; more are rounded. */
export function parseRate(text) {
  const s = String(text ?? '').trim().replace(/[$,\s]/g, '');
  if (s === '') return { ok: true, rate4: 0 };
  if (!/^\d*(\.\d*)?$/.test(s) || s === '.') return { ok: false, error: t('rate.err.read') };
  const v = Number(s);
  if (!Number.isFinite(v) || v > 1e9) return { ok: false, error: t('rate.err.big') };
  return { ok: true, rate4: Math.round(roundTo(v, 4) * 10000) };
}

Typed percentage

Exact

Reads a percentage such as 7.5 % into whole basis points.

bp = round(roundTo(v, 2) × 100)

Number, then roundTo 2 decimals, × 100, rounded to a whole number. More than 2 typed decimals are rounded away.

  • units.js
  • lines 333 to 341
  • TakeoffCraft 0.8.1
/** "7.5" or "7.5%" → 750 basis points. 0–1000%. */
export function parsePercent(text) {
  const s = String(text ?? '').trim().replace(/[%\s]/g, '');
  if (s === '') return { ok: true, bp: 0 };
  if (!/^\d*(\.\d*)?$/.test(s) || s === '.') return { ok: false, error: t('pct.err.read') };
  const v = Number(s);
  if (v > 1000) return { ok: false, error: t('pct.err.big') };
  return { ok: true, bp: Math.round(roundTo(v, 2) * 100) };
}

Quantity × rate to the cent

64-bit

The extended amount of a line in whole cents.

cents = round(qty × rate4 ÷ 100)

qty × rate4 ÷ 100 is one 64-bit product and one division, then rounded half away from zero by roundTo with its 1e-9 relative nudge. The quantity is a measured 64-bit value, so the product is rounded before it becomes a whole cent; from there on amounts are integers.

  • units.js
  • lines 357 to 360
  • TakeoffCraft 0.8.1
/** Extended amount in cents: quantity × unit rate, rounded half away from zero to the cent. */
export function extendCents(qty, rate4) {
  return Math.round(roundTo(qty * rate4 / 100, 0));
}

Quantity with waste

64-bit

A quantity increased by a waste percentage.

qtyW = qty × (1 + bp ÷ 10000)

bp ÷ 10000 is not exact in binary for most values, 1 is added, and the quantity is multiplied once. Three rounded operations; the result is not rounded further.

  • units.js
  • lines 361 to 362
  • TakeoffCraft 0.8.1
/** Quantity with waste: qty × (1 + bp / 10000). */
export const withWaste = (qty, bp) => qty * (1 + bp / 10000);

Cents as dollars text

Exact

Writes whole cents as $1,234.56.

Integer arithmetic only: Math.round, floor(a ÷ 100) and a mod 100. No decimal fraction is formed.

  • units.js
  • lines 342 to 347
  • TakeoffCraft 0.8.1
/** Integer cents → "$1,234.56". */
export function formatCents(c) {
  const neg = c < 0;
  const a = Math.abs(Math.round(c));
  return (neg ? '-' : '') + '$' + String(Math.floor(a / 100)).replace(/\B(?=(\d{3})+(?!\d))/g, ',') + '.' + String(a % 100).padStart(2, '0');
}
takeoffcraft.js TakeoffCraft 0.8.1 Exact 1 calculation Reviewed

Tests bin/test-takeoffcraft.mjs

Typed bid amount to cents

Exact

Reads an amount typed into the bid leveling sheet as whole cents.

cents = round(rate4 ÷ 100)

The amount is parsed by parseRate into whole 1 ÷ 10000 dollars and divided by 100 with Math.round. A typed third or fourth decimal is rounded to the cent here; an exact half cent rounds up.

  • takeoffcraft.js
  • line 3601
  • TakeoffCraft 0.8.1
const cents = (v) => { const r = U.parseRate(v); return r.ok ? { ok: true, c: Math.round(r.rate4 / 100) } : r; };

In detail

Method
rate4 = round(dollars × 10000); cents = round(quantity × rate4 ÷ 100), half away from zero; quantity with waste = quantity × (1 + bp ÷ 10000).
Number types
Integers held in 64-bit numbers for rates, cents and basis points; the quantity is a 64-bit number.
Tolerances
Rounding uses roundTo's nudge of 1e-9 × max(1, value), relative.
Limits
A rate above 1e9 dollars and a percentage above 1000 are refused. An amount within 1e-9 (relative) below a half cent rounds up.

Approach: 64-bit, direct

Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.

Every policy that takes this approach