Skip to content

BigNumber Fixed-Point Arithmetic Safety Wrapper for Multi-Tariff Decimal Scaling #53

Description

@elizabetheonoja-art

Problem Statement / Feature Objective

Resource consumption values in the utility dashboard span multiple decimal precisions: water in liters (integer), electricity in kWh (3 decimals), gas in therms (4 decimals), and financial costs in fiat (2 decimals with dynamic currency scaling). The current JavaScript Number-based arithmetic produces cumulative rounding errors that grow unboundedly over aggregation windows, leading to discrepancies of up to 0.05% in daily totals. A BigNumber fixed-point arithmetic safety wrapper must provide precise decimal arithmetic with explicit precision configuration, overflow detection, and seamless serialization for Redux state.

Technical Invariants & Bounds

  • Supported precisions: 0 (integer), 2 (currency), 3 (electricity), 4 (gas), 6 (fine-grained sub-metering), 9 (nanoscale).
  • Internal representation: BigNumber.js (or decimal.js) with default precision = 28 significant digits; rounding mode ROUND_HALF_UP.
  • Overflow detection: an explicit max value of 10^21 per resource (1 trillion units at 9 decimals). Any operation resulting in a value exceeding this threshold throws a SafetyRangeError and surfaces a UI warning.
  • Serialization: toRedux() outputs { value: string, precision: number, scale: number } to avoid floating-point corruption in Redux DevTools.
  • Operator overloads: add, sub, mul, div, eq, gt, gte, lt, lte with type safety via a branded type Decimal.

Codebase Navigation Guide

  • src/utils/decimal.ts - Core Decimal branded type and arithmetic functions.
  • src/types/meter.ts - Type definitions for MeterReading, ResourceConsumption, TariffRate using the Decimal type.
  • src/utils/aggregation.ts - Existing aggregation logic; must be refactored to use Decimal instead of number.
  • src/store/slices/resourceSlice.ts - Redux slice storing consumption data; serialization/deserialization with Decimal.
  • src/components/dashboard/ResourceChart.tsx - Chart component rendering time-series data; must accept Decimal values.
  • src/hooks/useResourceData.ts - Data fetching hook; converts API responses (string-encoded decimals) to Decimal type.

Implementation Blueprint

  1. Create src/utils/decimal.ts: export a branded type Decimal

    = { readonly __brand: unique symbol } & BigNumber. Provide factory functions: decimal(value: string | number, precision: P): Decimal

    , and arithmetic functions: add, sub, mul, div, min, max, sum. Each function validates precision compatibility at runtime (throw if mismatched).

  2. Define a safety check in each arithmetic function: after computation, compare absolute value against MAX_SAFE = 10^21. If exceeded, throw SafetyRangeError with a descriptive message.
  3. Refactor aggregation.ts to replace all number arithmetic with Decimal calls. This includes sum, average, min, max, and cumulative totals. Ensure that intermediate results are not cast back to number.
  4. Update resourceSlice.ts: the Redux state stores consumption values as { value: string, precision: number }. Deserialize via decimal(payload.value, payload.precision) on read; serialize via { value: d.toFixed(p), precision: p } on write to the store.
  5. In ResourceChart.tsx, charting library (Chart.js or Recharts) typically expects numbers. Create a helper toDecimalNumber(d: Decimal

    ): number that converts at render time with explicit rounding, with a console.warn if precision loss > Number.EPSILON.

  6. Write unit tests in src/tests/decimal.test.ts covering: addition at mixed precision (upcast to higher precision), multiplication overflow, serialization round-trip, and 1,000-iteration accumulation to verify zero drift.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions