Decimal Precision Lost on Save
A product price entered as 19.99 is stored and returned as 20.0 after being saved through the application. The database column is defined as FLOAT — an IEEE 754 approximate numeric type — rather than DECIMAL or NUMERIC, which are exact types. Because 19.99 cannot be represented exactly in binary floating-point, the stored value differs from the input.
MediumIntermediateAPI testingManual testingDatabase testing
// UNDERSTAND
// Symptoms
- POST /api/products with price: 19.99 returns a record where price is 20.0 or 19.990000000000002
- Monetary totals calculated from stored prices are wrong by small but consistent amounts
- A price entered by the user does not round-trip correctly: the value shown after saving differs from what was submitted
- Multiple small rounding errors accumulate in invoice or order total calculations
// Root Cause
- The database column holding the price is defined as FLOAT or DOUBLE PRECISION — IEEE 754 binary floating-point types. These types cannot represent most decimal fractions exactly; 19.99 stored as a FLOAT is retrieved as the closest representable binary approximation, which differs from 19.99.
- The application layer converts the incoming price string to a JavaScript Number (also an IEEE 754 double) before sending it to the database, compounding the precision loss before the value is even written.
// Where It Appears
- Product and pricing tables where monetary values are stored as FLOAT instead of DECIMAL(10,2)
- Order total, invoice, and tax calculation fields that accumulate floating-point rounding errors
- Any column storing percentages, exchange rates, or measurement values that require exact decimal representation
- APIs generated from an ORM where the column type was not explicitly set and defaulted to a floating-point type
// REPRODUCE & TEST
// How to Reproduce
- 01Send POST /api/products with body { "name": "Test Item", "price": 19.99 } and a valid bearer token; note the returned product ID (e.g. 301)
- 02Send GET /api/products/301 and read the price field in the response
- 03Compare the returned price against the submitted value 19.99 — any difference confirms precision loss
// Test Data Needed
- Permission to create products via the API
- A decimal value that cannot be represented exactly as IEEE 754 — 19.99, 0.1, 0.7, and 4.2 are reliable test cases
// Manual Testing Ideas
- Enter a price of 19.99 in the product creation form, save it, then navigate to the product detail page and confirm the displayed price is exactly 19.99
- Test with several decimal values known to be problematic in binary floating-point: 0.1, 0.7, 4.2, 10.05 — any that round-trip incorrectly confirm the column type issue
- Create an order with multiple line items priced at values such as 0.10 each, check the computed total, and verify it equals the expected sum
- Check the database schema directly (if accessible) to confirm the column type — FLOAT or DOUBLE indicates the bug; DECIMAL(10,2) or NUMERIC indicates the correct type
// API Testing Ideas
- Send POST /api/products with body { "name": "Test Item", "price": 19.99 }; capture the returned product id (301)
- Send GET /api/products/301 and assert that the price field equals exactly 19.99 — not 20.0 or 19.990000000000002
- Repeat with price: 0.1 and assert GET returns exactly 0.1
- Repeat with price: 10.05 and assert GET returns exactly 10.05
- If any GET response returns a value that differs from the submitted value, the precision bug is confirmed
// Automation Idea
Send POST /api/products with each of the following prices: 19.99, 0.1, 0.7, 10.05. For each, capture the returned product ID and immediately send GET /api/products/{id}. Assert the price field in the GET response is strictly equal (===) to the submitted value. Any inequality confirms floating-point precision loss in storage or retrieval.
// Expected Result
A price of 19.99 submitted via POST /api/products is returned as exactly 19.99 by a subsequent GET /api/products/301, with no rounding or approximation.
// Actual Result (Example)
POST /api/products with { "price": 19.99 } returns id: 301. GET /api/products/301 returns { "price": 20.0 }. The submitted value 19.99 was rounded during storage because the database column is defined as FLOAT.
// REPORT IT
Example Bug Report
- Title
- Product price 19.99 stored and returned as 20.0 — FLOAT column causes precision loss
- Severity
- Medium
- Environment
- Staging environment Postman Valid bearer token Endpoint: POST /api/products and GET /api/products/301
- Steps to Reproduce
- 01Send POST /api/products with body { "name": "Test Item", "price": 19.99 } and a valid bearer token; note the returned id (301)
- 02Send GET /api/products/301 and read the price field in the response
- 03Compare the returned value against the submitted value 19.99
- Expected Result
- GET /api/products/301 returns price: 19.99 — the exact submitted value.
- Actual Result
- GET /api/products/301 returns price: 20.0. The value 19.99 was rounded on save because the database column is typed as FLOAT.
- Impact
- Monetary values are stored and displayed incorrectly. Price discrepancies erode user trust and can cause billing errors. In financial applications, even small floating-point errors accumulate across millions of transactions.