IEEE 754 floating-point arithmetic cannot represent most decimal fractions exactly. Storing 19.99 as a float in PostgreSQL or MongoDB introduces rounding errors that compound across thousands of transactions — a classic fintech data-integrity failure cited under CWE-681 (Incorrect Conversion Between Numeric Types) and CWE-682 (Incorrect Calculation). At scale, accumulated errors produce off-by-one-cent discrepancies in statements, reconciliation failures, and potential regulatory exposure under GAAP precision requirements. Integer cent storage eliminates the issue entirely: 1999 is exact in every language and database type system.
Critical because floating-point accumulation in monetary storage produces unbounded rounding drift that corrupts financial records and triggers compliance failures.
Change all monetary database columns to integer types and add an explicit cents-conversion step in the API layer before any write. Reference prisma/schema.prisma for schema changes.
// prisma/schema.prisma
model Transaction {
id String @id @default(cuid())
amountCents Int // never Float or Decimal
currency String
}
// src/lib/currency.ts
export function toCents(decimalString: string): number {
return Math.round(parseFloat(decimalString) * 100);
}
export function fromCents(cents: number): string {
return (cents / 100).toFixed(2);
}
ID: finserv-form-validation.currency-amount.integer-cents-storage
Severity: critical
What to look for: Enumerate all database columns or document fields that store monetary values. For each, quote the column type from the schema file (e.g., amount DECIMAL(10,2) or amountCents Int). Count the total monetary columns and classify each as integer-safe (bigint, integer, int) or float-unsafe (float, double, decimal, numeric, real). Report the ratio: "X of Y monetary columns use integer storage."
Pass criteria: At least 100% of monetary columns use integer types (bigint, integer, or int) storing values in cents. No more than 0 monetary columns use float, double, or decimal types. Form inputs accept decimal notation (e.g., "19.99") but a conversion to integer cents exists before storage. Report even on pass: "X of Y monetary columns store integer cents."
Fail criteria: Any monetary column uses float, double, decimal, or numeric type, or no conversion from decimal input to integer cents exists in the API layer.
Do NOT pass when: The schema uses DECIMAL(10,2) or Decimal — these still carry floating-point semantics in many ORMs. Only true integer types count as pass.
Skip (N/A) when: The project has no financial data storage (no database schema found, or 0 monetary columns detected).
Detail on fail: Identify the storage pattern. Example: "Database schema stores amount as DECIMAL(10,2) — prone to floating-point rounding errors. Should be stored as INTEGER (cents)." or "API accepts '19.99' and stores it directly as float in MongoDB without converting to cents"
Cross-reference: The Database Schema & Migrations audit covers column type choices and migration safety that complement financial storage patterns.
Remediation: Floating-point math causes unpredictable rounding errors in financial systems. Always store monetary amounts as integers representing the smallest currency unit. In prisma/schema.prisma or src/db/schema.ts, change column types:
Database schema (Prisma) in prisma/schema.prisma:
model Transaction {
id String @id @default(cuid())
amountCents Int // Store as integer cents, never float
currency String // e.g., "USD", "EUR"
}
Conversion logic in src/lib/currency.ts:
// Input: "19.99" (user-entered decimal string)
const amountCents = Math.round(parseFloat(userInput) * 100);
// Now store amountCents (1999) in database as integer
// Display: Convert back to decimal when showing to user
const displayAmount = (amountCents / 100).toFixed(2); // "19.99"