Resources Numerical accuracy Calculation policy

Rates exported as a price table

Writes each priced item's material and labor rates as a price table another app can read.

How the precision is kept

Rates are whole 1 ÷ 10000 dollars in the takeoff. For the table they are divided by 10000, converted to the table's unit and rounded to 6 decimals. For units that convert by 1 the rounding changes nothing; for a rate per square yard it does.

Open item. Keep the converted rate unrounded in the price table; rounding a per-square-yard rate ÷ 9 to 6 decimals changes it by up to half a millionth of a dollar per square foot.

Calculations under this policy

2 calculations in 1 source file. 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.

carbon.js TakeoffCraft 0.8.1 64-bit 2 calculations Reviewed

Tests tools/takeoffcraft-test/browser-r3.mjs

Unit conversions for the price table

Exact

The table unit and factor for each takeoff unit.

The factors are 1, 1 ÷ 9 and 1 ÷ 100, the defined ratios. The two reciprocals are rounded once in 64-bit when the table is built.

  • carbon.js
  • line 92
  • TakeoffCraft 0.8.1
const PRICE_UNIT = Object.freeze({ LF: ['lf', 1], m: ['m', 1], SF: ['sf', 1], SY: ['sf', 1 / 9], SQ: ['sf', 1 / 100], 'm²': ['m2', 1], EA: ['item', 1], CY: ['cy', 1], 'm³': ['m3', 1] });

Rates converted and rounded

64-bit

One table row per priced item, with its material rate, labor rate and waste.

rate = round(rate4 ÷ 10000 × factor × 10⁶) ÷ 10⁶

mat and lab are rate4 ÷ 10000 × factor, then Math.round(… × 1e6) ÷ 1e6. A whole-cent or 4-decimal rate with factor 1 is unchanged by this; a per-square-yard rate becomes a rounded ninth. Waste is basis points ÷ 10000 clamped to 0 … 5.

  • carbon.js
  • lines 95 to 112
  • TakeoffCraft 0.8.1
export function priceRows(est, items) {
  const rows = [], skipped = [], seen = new Set();
  const nameOf = new Map((items || []).map((i) => [i.id, i.name]));
  for (const r of est.rows) {
    if ((r.kind !== 'item' && r.kind !== 'condition') || r.error) continue;
    const name = nameOf.get(r.itemId) || r.name;
    const label = r.kind === 'condition' ? name + ' ' + r.type : name;
    const u = PRICE_UNIT[r.unit];
    const mat = (Number(r.mat) || 0) / 10000, lab = (Number(r.lab) || 0) / 10000;
    if (!u) { skipped.push({ name: label, why: 'unit' }); continue; }
    if (!(mat > 0 || lab > 0)) { skipped.push({ name: label, why: 'norate' }); continue; }
    let id = slug(label), n = 2;
    while (seen.has(id)) id = slug(label).slice(0, 52) + '-' + n++;
    seen.add(id);
    rows.push({ id, match: [norm(name).slice(0, 60)].filter(Boolean), classes: [], unit: u[0], density: null, mat: Math.round(mat * u[1] * 1e6) / 1e6, lab: Math.round(lab * u[1] * 1e6) / 1e6, waste: Math.min(5, Math.max(0, (Number(r.waste) || 0) / 10000)), source: 'TakeoffCraft rates', note: '' });
  }
  return { rows: rows.filter((x) => x.match.length), skipped };
}

In detail

Method
dollars = rate4 ÷ 10000; per-unit conversion × 1, × 1 ÷ 9 (square yard to square foot) or × 1 ÷ 100 (roofing square to square foot); rounded to 6 decimals; waste = basis points ÷ 10000, kept between 0 and 5.
Number types
64-bit numbers; the rates start as integers.
Limits
A rate per square yard divided by 9 is rounded to the nearest 0.000001 dollar. Acres and hectares have no entry in the table's units and are left out with the reason 'unit'.

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