A bill must be correct and explainable
An electricity bill begins with two cumulative meter readings. Their difference is the consumption for the billing period. The difficult part is not subtraction; it is applying each tariff rate only to the units that belong to its slab, validating impossible readings and presenting each component clearly enough for a consumer to verify.
Measure
Units consumed = current reading − previous reading.
Price
Split consumption across progressive tariff slabs.
Explain
Show energy, fixed, surcharge and final amounts separately.
Turn policy statements into testable conditions
| Requirement | Rule | Failure response |
|---|---|---|
| Consumer identity | Number and name must not be empty | Ask again |
| Meter readings | Both readings are non-negative whole numbers | Reject invalid input |
| Reading sequence | Current reading ≥ previous reading | Stop without producing a bill |
| Consumption | units = current − previous | Never accept units independently |
| Energy charge | Charge each block at its own rate | Protect exact slab boundaries |
| Surcharge | 5% only when energy charge > ₹1,500 | No surcharge at exact equality |
Separate tariff policy from bill data
TariffSlab
upperLimit marks the cumulative end of a slab; rate stores its price per unit. A negative upper limit represents the unlimited final slab.
ElectricityBill
Stores consumer identity, readings, derived units and every monetary component. One structure can be passed safely between calculation and display functions.
Input fields → validated readings → derived units → energy charge
↓
fixed + surcharge
↓
payable totalWhy keep derived values?
Saving the energy charge, surcharge and total inside the bill makes the printed result auditable. In a larger system, these values would also form a billing snapshot: later tariff changes would not silently rewrite an already issued bill.
Charge blocks—not all units at the last rate
| Consumption block | Units available in block | Example rate |
|---|---|---|
| 0–100 | 100 | ₹1.50 |
| 101–200 | 100 | ₹2.50 |
| 201–500 | 300 | ₹4.00 |
| Above 500 | Unlimited | ₹6.00 |
Worked example: 650 units
- First 100: 100 × ₹1.50 = ₹150.
- Next 100: 100 × ₹2.50 = ₹250.
- Next 300: 300 × ₹4.00 = ₹1,200.
- Remaining 150: 150 × ₹6.00 = ₹900.
- Energy charge = ₹2,500; surcharge = 5% = ₹125.
- Total = ₹2,500 + ₹75 fixed charge + ₹125 = ₹2,700.
Give each function one reason to change
| Function | Responsibility | Important guarantee |
|---|---|---|
readNonEmptyLine | Read bounded text using fgets | No empty identity |
readNonNegativeInt | Read and validate a meter value | Value ≥ 0 |
calculateEnergyCharge | Walk tariff slabs | Each unit priced once |
prepareBill | Derive every computed field | Reject reversed readings |
printBill | Format the final breakdown | No calculation mixed with UI |
Tariffs are stored in an array rather than a long if/else chain. Adding or changing a slab is therefore mainly a data change, while the calculation loop remains stable.
Compiler-ready C11 program
The program accepts a consumer number, a name and two meter readings. It performs bounded text input, rejects invalid numbers, calculates a progressive charge and prints a complete breakdown.
Loading source…Trace: readings 4,250 and 4,900
- The meter readings pass basic validation.
- prepareBill accepts the reading order.
- Consumption is derived, not typed separately.
- Calculate the first block.
- Calculate the second block.
- Calculate the third block.
- Price the units above 500.
- Apply the threshold rule.
- Add energy, fixed charge and surcharge.
- Display an auditable result.
Press Next to begin.
Test immediately before and after every slab boundary
| Units | Expected energy charge | Why it matters |
|---|---|---|
| 0 | ₹0.00 | Zero-consumption case |
| 100 | ₹150.00 | End of slab 1 |
| 101 | ₹152.50 | First unit in slab 2 |
| 200 | ₹400.00 | End of slab 2 |
| 201 | ₹404.00 | First unit in slab 3 |
| 500 | ₹1,600.00 | End of slab 3; surcharge applies |
| 501 | ₹1,606.00 | First unit in final slab |
| 650 | ₹2,500.00 | Uses all four slabs |
Invalid reading order
Invalid numeric input
Surcharge boundary
>, so equality must not add a surcharge.The algorithm scales with the number of slabs
| Operation | Time | Extra space | Reason |
|---|---|---|---|
| Validate readings | O(1) | O(1) | Constant comparisons |
| Calculate energy charge | O(s) | O(1) | Visits at most s tariff slabs |
| Prepare bill | O(s) | O(1) | Calculation dominates |
| Print bill | O(1) | O(1) | Fixed number of fields |
Here s = 4, so runtime is effectively constant. The sample uses double for readability. Financial production software often stores the smallest currency unit as an integer or uses a decimal type so binary floating-point rounding cannot accumulate unnoticed.
Check your reasoning, then extend the system
For 201 units, how many units are charged at ₹4.00?
Which invariant protects the consumption calculation?
Build the next version
- Print a slab-by-slab charge breakdown on the receipt.
- Add domestic and commercial tariff plans without duplicating the calculation loop.
- Store many consumers in a file and search by consumer number.
- Add billing dates, due dates and a clearly specified late-fee rule.
- Create automated tests for every slab and surcharge boundary.
Questions your project should answer
Why use an array of tariff slabs?
It separates policy data from control flow. The calculation loop can process any ordered set of slabs, which reduces repeated conditions and makes tariff changes easier to review.
Why derive units from readings?
Both readings are observable meter states. Accepting a separately typed consumption value creates two sources of truth that could disagree.
What is the most likely boundary bug?
Charging the 100th or 200th unit twice, or moving it into the next slab too early. Tests at boundary−1, boundary and boundary+1 expose the error.
Why not use float for money?
Float has lower precision. Double is better for this educational example, but production billing should follow a defined decimal or integer-smallest-unit policy with explicit rounding.
How would you make issued bills reproducible?
Store the exact tariff version and computed line items with each bill. Recalculating an old bill using today's tariff could produce a different result.
Correct software makes policy visible
This project connects structures, arrays, functions, input validation, loops and formatted output to a real decision process. Its strongest lesson is that business rules must become explicit invariants and boundary tests—not assumptions hidden inside arithmetic.
