# Postmortem: Decimal Input Overflow in Calculator Application
## Summary
A calculator application allowed users to input unlimited decimal digits via button presses despite UI-level safeguards. The attempted solution prevented duplicate decimal points but failed to enforce single-digit precision after the decimal separator in numeric inputs.
## Root Cause
1. Validation scope was incomplete:
- Only the decimal separator button (`btnDot`) was restricted
- Digit input buttons had no validation logic
2. Flawed validation condition:
- Guard clause only triggered if **exactly 1 digit** existed after decimal point
- Allowed input beyond first decimal digit
3. State inspection limitations:
- No centralized validation of the full input state during digit entry
## Why This Happens in Real Systems
- **Partial input validation**: Developers often focus validation on specific controls rather than holistic input state
- **Stateless checks**: Input handlers sometimes lack awareness of overall field context
- **Incremental development**: Features evolve without revisiting validation boundaries
- **Assumption gap**: Expecting UI controls to intrinsically limit input formatting
## Real-World Impact
1. Data corruption: Calculations using over-precise values produce incorrect results
2. UI confusion: Users see unformatted numbers (e.g., `4.999999` instead of `5.0`)
3. System failure: Downstream systems might reject malformed numbers
4. Validation breaches: Business rules requiring single-decimal precision bypassed
## Example Code
### Problematic Implementation
```csharp
private void btnDot_Click(object sender, EventArgs e)
{
if (!txtDisplay.Text.Contains("."))
{
txtDisplay.Text += ".";
}
else
{
int dotIndex = txtDisplay.Text.IndexOf(".");
int digitsAfterDot = txtDisplay.Text.Length - dotIndex - 1;
if (digitsAfterDot >= 1) // Only prevents action when ≥1 digit exists
{
return; // Doesn't prevent subsequent digit inputs
}
}
}
// Missing validation in digit handlers:
private void btnDigit_Click(object sender, EventArgs e)
{
// No decimal position checks
txtDisplay.Text += ((Button)sender).Text;
}
Corrected Implementation
private void AddToInput(string value)
{
if (txtDisplay.Text.Contains("."))
{
int decimalPos = txtDisplay.Text.IndexOf(".");
if (txtDisplay.Text.Length - decimalPos > 1) // Already has 1 decimal digit
{
return; // Block additional decimal digits
}
}
txtDisplay.Text += value;
}
// All button handlers route through centralized validation:
private void btnDigit_Click(object sender, EventArgs e)
=> AddToInput(((Button)sender).Text);
private void btnDot_Click(object sender, EventArgs e)
=> AddToInput(".");
How Senior Engineers Fix It
- Implement input centralization:
- Create single validation method for all input sources
- Route all button handlers through unified logic
- Track input state:
- Maintain explicit decimal presence flag
- Monitor current decimal precision level
- Apply defensive parsing:
- Use
decimal.TryParse for type safety
- Combine with regex validation (e.g.,
^\d*\.?\d{0,1}$)
- Implement input masking:
- Use
TextBox events (TextChanged) for real-time filtering
- Design boundary tests:
- Verify edge cases (0.0, 0.5, 10.9, 123.0)
- Simulate rapid decimal/digit alternation
Why Juniors Miss It
- Event-centric thinking: Focuses on individual button events rather than holistic input state
- UI illusion: Assumes input controls inherently restrict invalid states
- Scope misalignment: Solves for preventing duplicate decimals but ignores digit behavior
- Boundary blindness: Fails to test single-digit post-decimal scenarios
- State-tracking avoidance: Prefers simple boolean checks over precise digit counting