fix: fix floating point precision error

This commit is contained in:
Björn Fromme
2026-03-16 11:59:11 +01:00
parent 2403f8fb29
commit 7d6a80eefa
2 changed files with 40 additions and 3 deletions
+37
View File
@@ -89,6 +89,42 @@ Services from BPN API may lack complete data (especially prices). Always enrich
### Notification System
Field handlers generate notifications (auto-changes) → collected by controller → sent via HX-Trigger → displayed as toasts.
### Floating-Point Precision in Price Calculations
**Critical**: All monetary comparisons must account for floating-point arithmetic accumulation errors.
**The Problem:**
- Prices are parsed from XML as German-formatted strings (e.g., `"1.812,70"`)
- Multiple price additions (rooms + services + insurances) accumulate tiny precision errors (`~1e-15` per operation)
- With bulk insurance and deep calculation chains, errors compound to `~1e-13` or larger
- Example: API returns `1812.7`, calculation produces `1812.6999999999998`
**The Solution:**
- **Always round monetary values to 2 decimal places before comparison**
- Use `round($price, 2)` for cent precision (standard for EUR currency)
- Never use strict equality (`===` or `!==`) on unrounded float prices
**Implementation Example (Step3Controller:93-94):**
```php
// CORRECT: Round to cent precision before comparison
$apiTotal = round($inquiryResponse->totalPrice ?? 0.0, 2);
$calculatedTotal = round($this->priceCalculator->calculateGrandTotal($bookingCreateDto), 2);
if ($apiTotal !== $calculatedTotal) {
// Handle mismatch
}
// WRONG: Direct float comparison (will fail due to precision errors)
if ($inquiryResponse->totalPrice !== $this->priceCalculator->calculateGrandTotal($bookingCreateDto)) {
// This comparison is unreliable!
}
```
**Important Notes:**
- Rounding eliminates precision errors smaller than 1 cent (€0.01)
- The BPN API returns prices already rounded to cent precision
- This is the industry-standard approach for financial calculations
- Only affects comparison logic - does not alter actual price calculation flow
### Dirty State Detection (Edit Mode)
Fingerprint-based change detection to warn users about unsaved modifications:
- **BookingFingerprintService** generates SHA-256 hash of all mutable booking data
@@ -345,6 +381,7 @@ ddev exec "php -r 'opcache_reset()';" # Clear opcache after code cha
- **Edit mode service availability**: Services with `available <= 0` remain visible and editable for participants who already have them (prevents fingerprint false positives)
- **Session cleanup on exit**: All exit paths from edit mode (save, discard, cancel) properly clear session to reset dirty state
- **Participant naming convention**: Index 0 is "Anmelder:in", others are "Teilnehmer:in N" (1-based, not 0-based)
- **Floating-point price comparisons**: ALWAYS use `round($price, 2)` before comparing monetary values to avoid precision errors (see "Floating-Point Precision in Price Calculations" section)
## References