Skip to content

Financial calculations use floating-point math instead of fixed-point/decimal — cumulative rounding drift #451

Description

@Wilfred007

What

Introduce a shared fixed-point/decimal money type (or a well-tested decimal library) for payroll-related financial math, and migrate the repeated parseFloat/toFixed pattern across tax, benefits, and bonus calculations to use it.

Why

Three separate services repeat the same anti-pattern of rounding each intermediate value to a fixed number of decimals via .toFixed() and then accumulating results with native JS floating-point addition:

  • backend/src/services/taxService.tscalculateDeductionsRuleBased (L242-278): deductedAmount = parseFloat((grossAmount * (Number(rule.value) / 100)).toFixed(7)), then totalTax += deductedAmount accumulated as a JS number
  • backend/src/services/benefitsService.ts — L64: parseFloat(n.toFixed(7))
  • backend/src/services/payrollBonusService.ts — L222-227: .reduce((sum, item) => sum + parseFloat(item.amount), 0)

JS floats can't exactly represent most decimals. Rounding each line item to 7 decimals (matching Stellar's stroop precision) and then summing with float addition across an arbitrary number of stacked deduction/tax rules lets rounding error accumulate — net payroll amounts submitted on-chain can silently diverge from gross minus deductions by fractions of a stroop, compounding across pay periods and audit reports.

Scope

In scope:

  • Introduce (or adopt an existing) fixed-point/decimal type for all money math in these three services
  • Migrate the specific call sites listed above to use it consistently
  • Tests demonstrating the old code's drift on a stacked multi-rule example, and that the new code doesn't drift

Out of scope:

  • Contract-side (Rust) math — that's tracked separately (#398 covers revenue_split dust/rounding)
  • Non-financial numeric calculations elsewhere in the backend

Acceptance Criteria

  • A shared decimal/fixed-point money type exists and is used by taxService.ts, benefitsService.ts, payrollBonusService.ts
  • Summing N stacked deduction/tax rules produces an exact result (no drift), verified by a regression test that fails against the old float-based code
  • Existing tax/benefits/bonus tests still pass with correct (not just similar) values

Technical Context

  • backend/src/services/taxService.ts L242-278
  • backend/src/services/benefitsService.ts L64
  • backend/src/services/payrollBonusService.ts L222-227

Activity

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

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third CampaignbackendBackend developmenthardComplex tasks

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions