Skip to content

Trace: arithmetic on single values prints bare coherent-SI numbers with no unit #2

Description

@christianparpart

Summary

A trace step computed from single values by arithmetic (*, /, +, -) prints its value in the coherent SI unit with no unit symbol, even when an operand is shown in a named unit such as grams. The number is correct, but it reads as dimensionless and on a different scale from the lines around it, so a reader cannot check it by eye.

Example

From the outlier-rejection example in docs/statistics.md (the same trace is pinned in test/rejection_tests.cpp and docs/gallery.md):

2. 3/50
3. pass mean = 413/10 g
4. #2 * #3 = 1239/500000
...
6. rejected element 4 of 6 (44 g) in pass 1: abs(x - mean) = 27/10 g > 1239/500 g (deviation from mean)

Line 4 is 3/50 × 41.3 g = 2.478 g. It is printed as 1239/500000, which is the value in kilograms, but with no unit. Two lines later the same value appears as 1239/500 g. Nothing on line 4 tells the reader it is a mass, or that it is in kg rather than g.

Current behaviour

  • Series steps already carry a readable unit. A series sum, range or running total, and a series scaled by a pure number, borrow their operand's unit through detail::operand_unit_or, guarded by detail::borrowable in include/formula-cpp/trace.hpp.
  • Single-value binary steps do not. They keep the coherent unit, whose symbol is empty, so the value prints bare.
  • detail::borrowable already encodes the safety rules any fix must keep:
    • never borrow a unit with an offset (°C), because a product, sum or difference of such readings is not a point on that scale and would be off by the offset;
    • never borrow a unit without a symbol.

Proposed behaviour

Show a single-value arithmetic step in a named unit when that is unambiguous and safe:

  • Scaling by a pure number (x * k, k * x, x / k, where k is dimensionless): show the result in the other operand's unit, if borrowable. Line 4 above would read #2 * #3 = 1239/500 g.
  • Sum or difference of like quantities (x + y, x - y, both operands in the same borrowable unit): show the result in that unit. Offset units stay excluded. A difference of two °C readings is a temperature interval, not a reading.
  • Otherwise (products or quotients of dimensioned quantities, mixed units, offset units): keep the coherent unit, and consider printing its derived symbol (e.g. kg, m/s) instead of nothing, so no computed value prints without a unit.

The rule should be read off the operand steps, as operand_unit_or does, never off types, so that what an operand step shows is what carries over.

Scope and cost

  • Mostly in RecordingSink's binary-node recording in trace.hpp, reusing borrowable.
  • Expect many pinned expectations to change. The bare coherent form appears in the gallery (docs/gallery.md), the statistics guide (docs/statistics.md), test/rejection_tests.cpp, and numerous other trace and render tests and guides. The gallery must be regenerated (gallery.is-current), and every docs quote must still match its example's output.
  • Step's layout need not change: the step already has a unit field.

Acceptance criteria

  1. The example above prints #2 * #3 = 1239/500 g.
  2. A sum of two gram values prints in grams; a difference of two °C readings does not print in °C.
  3. A product of two dimensioned quantities (e.g. mm × mm) prints either in the coherent unit with its symbol, or in a documented borrowed form, never as a bare number.
  4. No step ever prints a number in a unit different from the one it is shown with; add a test that re-derives each printed value from the step's SI value and shown unit.
  5. Tests pin each case; the gallery and docs are regenerated and pass their checks on all supported compilers.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions