Resources Numerical accuracy Calculation policy

Numbers typed into the property grid

Turns what a person types or drags in a Properties row into the number handed to the app.

How the precision is kept

A number row hands the app the 64-bit value of the typed text, or the value the app's own parser returns, limited to the row's minimum and maximum and not rounded. The box then shows that value with String, the shortest text that reads back to the same number. A slider row rounds its value to the decimals of its step plus two, at most 8.

Calculations under this policy

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

app-propgrid.js Shared shell, every app 64-bit 3 calculations Reviewed

Tests bin/test-app-propgrid.mjs

Value of a number row

64-bit

The number handed to the app when a person changes a number box.

v = min(max, max(min, typed value))

The typed text is converted with Number, or by the row's own parser when it has one, and only limited to the row's range. No rounding is applied, and the box is refilled with String(v).

  • app-propgrid.js
  • lines 214 to 229
ni.addEventListener('change', function () {
  var v;
  if (r.parse) {
    var pr = null;
    try { pr = ni.value.trim() === '' ? { ok: false, empty: true } : r.parse(ni.value); } catch (e) { pr = { ok: false }; }
    if (!pr || !pr.ok || !isFinite(pr.value)) { ni.setAttribute('aria-invalid', 'true'); try { onInvalid(r.id, pr, ni.value); } catch (e) { if (window.console) console.error(e); } return; }
    ni.removeAttribute('aria-invalid');
    v = Number(pr.value);
  } else {
    v = Number(ni.value);
    if (ni.value === '' || !isFinite(v)) { ni.value = r.value == null ? '' : String(r.value); return; }
  }
  if (r.min != null) v = Math.max(r.min, v);
  if (r.max != null) v = Math.min(r.max, v);
  ni.value = String(v); emit(r, v);
});

Rounding of a slider value

64-bit

Removes binary noise such as 0.30000000000000004 from a slider's value.

v = round to (decimals of the step + 2) decimals, at most 8

Rounds with toFixed and converts back with Number, so the value handed on has at most 8 decimals. The decimals are counted in String(step); a step below 1e-6 prints in exponent form with no decimal point and counts as 0, giving 2 decimals.

  • app-propgrid.js
  • lines 56 to 59
function fix(v, step) {   // no 0.30000000000000004 in a box
  var dec = String(step || 1).indexOf('.') === -1 ? 0 : String(step).split('.')[1].length;
  return Number(Number(v).toFixed(Math.min(dec + 2, 8)));
}

Value of a slider row

64-bit

The number handed to the app while a slider is dragged or its box is changed.

v = fix(clamp(value, min, max), step)

A value typed in the box is limited to the slider's range and then rounded by fix; a dragged value is rounded by fix. The rounded value is what the app receives, not the raw one.

  • app-propgrid.js
  • lines 194 to 203
var put = function (v) { if (v == null) { sl.value = r.min; nb.value = ''; } else { sl.value = v; nb.value = String(fix(v, r.step)); } };
put(r.mixed ? null : r.value);
if (r.mixed) nb.placeholder = T('shell.propgrid.varies');
sl.addEventListener('input', function () { var v = fix(sl.value, r.step); nb.value = String(v); emit(r, v, true); });
sl.addEventListener('change', function () { emit(r, fix(sl.value, r.step)); });
nb.addEventListener('change', function () {
  var v = Number(nb.value);
  if (nb.value === '' || !isFinite(v)) { put(r.value); return; }
  v = fix(clamp(v, r.min, r.max), r.step); put(v); emit(r, v);
});

In detail

Method
Number row: Number(text) or the row's parse function, then max(min, …) and min(max, …). Slider row: clamp, then toFixed(decimals of the step + 2, at most 8).
Number types
64-bit numbers.
Limits
A slider's value carries at most 8 decimals. A slider step that JavaScript writes in exponent form (below 1e-6) is read as having no decimals, so its values are rounded to 2 decimals.

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