Two calculation sites that both compute interest but use different rounding functions — one Math.round(), one Math.floor() — will produce different balances for identical inputs. The discrepancy shows up as a mismatch between the displayed APY and the actual accrued interest, leading to customer disputes and reconciliation failures. CWE-682 covers incorrect financial calculation; ISO 25010 maintainability requires that the same logical operation produce the same result regardless of call site.
Low because inconsistent rounding accumulates slowly and typically surfaces only during reconciliation or customer disputes, not as immediate transaction failures.
Centralize interest rounding into a single exported function in src/lib/finance.ts and replace every direct Math.round / Math.floor / .toFixed call in financial logic with it.
// src/lib/finance.ts
import Decimal from 'decimal.js';
/** Canonical rounding for all interest / APY calculations. */
export function roundInterestCents(value: number): number {
return new Decimal(value)
.toDecimalPlaces(0, Decimal.ROUND_HALF_EVEN)
.toNumber();
}
Search src/ for Math.round, Math.floor, and .toFixed within files that reference interest, apy, or rate, and replace each with roundInterestCents().
ID: finserv-form-validation.calculation-accuracy.interest-rounding-consistency
Severity: low
What to look for: Enumerate all interest/APY calculation sites in the codebase. For each, quote the rounding function used (e.g., Math.round, Math.floor, toDecimalPlaces(2, ROUND_HALF_EVEN)). Count the distinct rounding strategies observed. Report: "X interest calculation sites found using Y distinct rounding strategies."
Pass criteria: At least 100% of interest and APY calculation sites use the same rounding strategy. The strategy is documented (a constant, comment, or named function like bankersRound). No more than 1 distinct rounding strategy across all interest sites.
Fail criteria: At least 2 distinct rounding strategies are used across interest calculations, or no rounding strategy is documented.
Skip (N/A) when: The project has no interest or APY calculations (0 interest calculation sites found).
Detail on fail: Example: "APY calculation in dashboard uses Math.round() (round-half-up). Interest accrual in API uses Math.floor() (round-down). Inconsistency between display and actual accrual." or "No documented rounding strategy for interest"
Cross-reference: The bankers-rounding check verifies the correct algorithm is used; this check verifies it is used consistently everywhere.
Remediation: Document and centralize the rounding strategy in src/lib/finance.ts:
In src/lib/finance.ts:
export const ROUNDING_STRATEGY = 'banker'; // round-half-to-even
export function roundInterest(value) {
// Implement banker's rounding
const cents = Math.round(value * 100);
return cents / 100;
}
// Usage everywhere:
const interest = roundInterest(principal * rate);