Resources Numerical accuracy Calculation policy

Lengths, areas and volumes as text

Turns a length in millimeters, an area or a volume into text in the display unit a person picked.

How the precision is kept

Values stay in millimeters (or SI) inside the app; these functions only write text. The inch is 25.4 mm, the legal definition, and the foot is 12 × 25.4. Each value is divided once by its factor and then rounded to a fixed number of decimals for the unit, or to the nearest 1/16 inch. The rounding is the limit of what the text shows, not of the stored value.

Calculations under this policy

9 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.

display-units.js BimCraft 0.8.1 64-bit 9 calculations Reviewed

Tests tools/bimcraft-test/units-gh3a-09-test.mjs

A length as text

64-bit

Millimeters as feet and inches to 1/16 inch, or as a decimal number in mm, cm, m, inches or feet.

sixteenths = round(|mm| ÷ 25.4 × 16); feet = ⌊sixteenths ÷ 192⌋; inches = ⌊rest ÷ 16⌋

25.4 is the exact definition of the inch. The length is rounded once, to whole sixteenths, and everything after that is whole-number arithmetic, so 11 15/16 inches plus rounding rolls over to 1 foot correctly. Decimal units divide once and round with toFixed; a result of −0 is written as 0.

  • display-units.js
  • lines 23 to 43
  • BimCraft 0.8.1
/** Millimetres as text. `ftin`: feet-inches to 1/16" (fractions reduced); `mm`: whole millimetres (0.1 below 10). */
export function fmtLen(mm, units = 'ftin', { unit = false } = {}) {
  if (!Number.isFinite(mm)) return '';
  if (units !== 'ftin' && FACTOR[units]) {
    const value = mm / FACTOR[units];
    const digits = units === 'mm' ? (Math.abs(value) < 10 ? 1 : 0) : units === 'in' ? 3 : 2;
    const rounded = Number(value.toFixed(digits));
    return (Object.is(rounded, -0) ? 0 : rounded) + (unit ? ' ' + (units === 'ftdec' ? 'ft' : units) : '');
  }
  const neg = mm < 0;
  let sixteenths = Math.round(Math.abs(mm) / INCH * 16);
  const ft = Math.floor(sixteenths / 192);
  sixteenths -= ft * 192;
  const inch = Math.floor(sixteenths / 16);
  let frac = sixteenths - inch * 16, den = 16;
  while (frac && frac % 2 === 0) { frac /= 2; den /= 2; }
  const whole = ft || inch || !frac;   // 10'-0 1/4", but 1/4" alone
  const inText = (whole ? String(inch) : '') + (frac ? (whole ? ' ' : '') + frac + '/' + den : '') + '"';
  const s = ft ? ft + "'-" + inText : inText;
  return (neg && sixteenths + ft ? '-' : '') + s;
}

Unit factors

64-bit

How many millimeters, square millimeters and cubic millimeters one display unit is.

in = 25.4 mm; ft = 12 × 25.4 mm; cm = 10 mm; m = 1000 mm

The inch is written from its definition, 25.4 mm, and the foot is 12 times that, computed in 64-bit when the file loads. The metric factors are powers of ten.

  • display-units.js
  • lines 19 to 20
  • BimCraft 0.8.1
export const INCH = 25.4;
export const FACTOR = { mm: 1, cm: 10, m: 1000, in: INCH, ftdec: 12 * INCH };

A coordinate as a number

64-bit

A coordinate in millimeters as a number in the display unit, with a fixed count of decimals.

value = mm ÷ factor, to 1 (mm), 2 (cm, in) or 3 (m, ft) decimals

One division and one toFixed. Feet and inches show decimal feet here. The decimals are fixed per unit, so a state-plane coordinate in feet shows to 0.001 ft.

  • display-units.js
  • lines 48 to 55
  • BimCraft 0.8.1
/** The number of fmtCoord alone (the unit goes beside it): decimal feet for feet-inches. */
export function coordNumber(mm, units = 'ftin') {
  if (!Number.isFinite(mm)) return '';
  const u = units === 'ftin' ? 'ftdec' : units;
  const digits = { mm: 1, cm: 2, m: 3, in: 2, ftdec: 3 }[u] ?? 3;
  const rounded = Number(((mm) / (FACTOR[u] || 1)).toFixed(digits));
  return (Object.is(rounded, -0) ? 0 : rounded).toLocaleString('en-US', { minimumFractionDigits: digits, maximumFractionDigits: digits, useGrouping: false });
}

A small length to three figures

64-bit

A tolerance or a scale per pixel as text with three significant figures.

value = mm ÷ factor, toPrecision(3)

One division, then rounded to three significant figures for the text. Feet and inches show decimal inches.

  • display-units.js
  • lines 58 to 64
  • BimCraft 0.8.1
/** A small length (a scale in length per pixel, a tolerance) with three significant figures and its unit: feet-inches shows decimal inches. */
export function fmtFine(mm, units = 'ftin') {
  if (!Number.isFinite(mm)) return '';
  const u = units === 'ftin' ? 'in' : units;
  const v = Number((mm / (FACTOR[u] || 1)).toPrecision(3));
  return (Object.is(v, -0) ? 0 : v) + ' ' + unitMark(u);
}

An area or a volume as text

64-bit

Square or cubic millimeters as text in the matching square or cubic display unit.

value = v ÷ factor, to the unit's decimals; in² = 25.4² mm²; ft² = (12 × 25.4)² mm²; ft³ = (12 × 25.4)³ mm³

One division by the squared or cubed factor and one toFixed. The squared and cubed inch and foot factors are computed from 25.4 in 64-bit when the file loads, so each carries the rounding of its multiplications. Thousands separators are added only from 10,000 up.

  • display-units.js
  • lines 67 to 76
  • BimCraft 0.8.1
const AREA = { mm: ['mm²', 1, 0], cm: ['cm²', 100, 1], m: ['m²', 1e6, 2], in: ['in²', INCH * INCH, 1], ftdec: ['ft²', (12 * INCH) ** 2, 1] };
const VOL = { mm: ['mm³', 1, 0], cm: ['cm³', 1000, 0], m: ['m³', 1e9, 2], in: ['in³', INCH ** 3, 0], ftdec: ['ft³', (12 * INCH) ** 3, 1] };
const base = (units) => (units === 'ftin' ? 'ftdec' : AREA[units] ? units : 'mm');
const group = (v, digits) => v.toLocaleString('en-US', { minimumFractionDigits: digits, maximumFractionDigits: digits, useGrouping: Math.abs(v) >= 10000 });
const fmtPow = (table, v, units, withUnit) => {
  if (!Number.isFinite(v)) return '';
  const [mark, f, digits] = table[base(units)];
  const r = Number((v / f).toFixed(digits));
  return group(Object.is(r, -0) ? 0 : r, digits) + (withUnit === false ? '' : ' ' + mark);
};

From meters, square meters and cubic meters

64-bit

What calculations give in SI, handed to the same formatters.

mm = m × 1000; mm² = m² × 10⁶; mm³ = m³ × 10⁹

Each value is multiplied by a power of ten and then divided by the unit factor, two rounded steps instead of one. Both are far below the decimals the text shows.

  • display-units.js
  • lines 82 to 84
  • BimCraft 0.8.1
export const fmtM = (m, units = 'ftin') => lenMark(m * 1000, units);
export const fmtM2 = (m2, units = 'ftin', unit) => fmtAreaMm2(m2 * 1e6, units, unit);
export const fmtM3 = (m3, units = 'ftin', unit) => fmtVolMm3(m3 * 1e9, units, unit);

Lengths inside a message

64-bit

Rewrites plain "N mm" and "N m" inside a message into the display unit.

The number is read back from the text of the message, so it is the value as the message printed it. A message that already carries a foot or inch mark is left as written, and m², m³ and rates are not touched.

  • display-units.js
  • lines 86 to 94
  • BimCraft 0.8.1
/**
 * A refusal's limits ("at least 10 mm thick", "up to 12 m") in the display unit. Only plain "N mm" / "N m" are rewritten
 * (never m² m³ m/s), and a message that already gives feet-inches or inches (3'-4", 4") is left as written.
 */
export function localizeLengths(text, units = 'ftin') {
  const s = String(text ?? '');
  if (/\d\s?['"]/.test(s)) return s;
  return s.replace(/(\d+(?:\.\d+)?) ?(mm|m)\b(?![²³\/])/g, (all, n, u) => lenMark(Number(n) * (u === 'm' ? 1000 : 1), units));
}

Quantities inside a sentence

64-bit

Rewrites lengths, areas and volumes inside a calculation's sentence into the display unit.

The number is parsed from text that was already rounded when the sentence was written, then converted and rounded again. The converted figure therefore carries the first rounding as well as its own. Units after a slash (W/m², kg/m³, m/s) and mm², mm³ are left alone.

  • display-units.js
  • lines 96 to 110
  • BimCraft 0.8.1
/**
 * A calculation's sentence in the pick: lengths ("4.2 m", "300 mm"), areas ("24 m²") and volumes ("60 m³") after a number.
 * A rate or density ("W/m²", "kg/m³", "m/s", "kN/m", "mm²/m") has no number straight before its unit and is left alone. A
 * sentence that already carries feet-inches marks is left as written.
 */
export function localizeQuantities(text, units = 'ftin') {
  const s = String(text ?? '');
  if (/\d\s?['"]/.test(s)) return s;
  return s.replace(/(-?\d+(?:\.\d+)?) ?(mm|m)(²|³)?(?![A-Za-z\/²³])/g, (all, n, u, pow) => {
    const v = Number(n);
    if (!pow) return lenMark(v * (u === 'm' ? 1000 : 1), units);
    if (u === 'mm') return all;   // mm² / mm³ are the calculation's own (bar areas), not a room's
    return pow === '²' ? fmtM2(v, units) : fmtM3(v, units);
  });
}

A paper size as text

64-bit

A sheet's width and height in the display unit.

value = mm ÷ factor, to 1 (mm, cm), 2 (in) or 3 (m, ft) decimals

One division and one toFixed per side. The paper table is in millimeters, with Letter as 215.9 × 279.4, which is 8.5 × 11 inches exactly.

  • display-units.js
  • lines 112 to 119
  • BimCraft 0.8.1
/** A paper size's dimensions in the pick (the paper's name, A3 or Letter, stays as it is). Feet-inches → decimal inches. */
export function fmtPaper(wMm, hMm, units = 'ftin') {
  if (!Number.isFinite(wMm) || !Number.isFinite(hMm)) return '';
  const u = units === 'ftin' ? 'in' : units;
  const d = { mm: 1, cm: 1, m: 3, in: 2, ftdec: 3 }[u] ?? 1;
  const n = (v) => String(Number((v / FACTOR[u]).toFixed(d)));
  return n(wMm) + ' × ' + n(hMm) + ' ' + unitMark(u);
}

In detail

Method
value in the unit = millimeters ÷ factor, then toFixed to the unit's decimals. Feet and inches: the length is turned into whole sixteenths of an inch with Math.round, then split into feet, inches and a reduced fraction by integer steps.
Number types
64-bit numbers. Sixteenths of an inch are whole numbers once rounded.
Tolerances
None. Decimals shown: mm 0 (1 below 10 mm), cm 2, m 2, inches 3, decimal feet 2 for lengths; coordinates mm 1, cm 2, m 3, inches 2, feet 3; areas m² 2, ft² 1, in² 1, cm² 1, mm² 0; volumes m³ 2, ft³ 1, others 0.
Limits
The text carries only the decimals listed. Two functions rewrite numbers found inside a sentence: they convert the number as printed there, which was already rounded when the sentence was written.

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