Compound interest: future value, with periodic compounding and a continuous-compounding variant
Pure numeric handlers, tolerance-based float assertions (never == on floats).
A Soma program from the finance part of the verified corpus: 46 lines, cells CompoundInterest, CompoundTests. It passes soma check with soma 3.2.4.
How it is built
The cell CompoundInterest declares 4 signals in its face: future_value(principal: Float, rate: Float, years: Int) -> Float, future_value_n(principal: Float, rate: Float, n: Int, years: Int) -> Float, continuous(principal: Float, rate: Float, years: Float) -> Float and doubling_time(rate: Float) -> Float. Handlers: future_value (line 14), future_value_n (line 19), continuous (line 24) and doubling_time (line 29).
The file also holds 1 test cell (CompoundTests) with 4 assertions, run by soma test.
The source
compound_interest.cell
// Compound interest: future value, with periodic compounding and a
// continuous-compounding variant. Pure numeric handlers, tolerance-based
// float assertions (never == on floats).
cell CompoundInterest {
face {
signal future_value(principal: Float, rate: Float, years: Int) -> Float
signal future_value_n(principal: Float, rate: Float, n: Int, years: Int) -> Float
signal continuous(principal: Float, rate: Float, years: Float) -> Float
signal doubling_time(rate: Float) -> Float
}
// FV = P * (1 + r)^t (annual compounding)
on future_value(principal: Float, rate: Float, years: Int) {
return principal * pow(1.0 + rate, years)
}
// FV = P * (1 + r/n)^(n*t) (n compounding periods per year)
on future_value_n(principal: Float, rate: Float, n: Int, years: Int) {
return principal * pow(1.0 + rate / n, n * years)
}
// FV = P * e^(r*t) (continuous compounding)
on continuous(principal: Float, rate: Float, years: Float) {
return principal * exp(rate * years)
}
// Rule of ln(2): years to double = ln(2) / ln(1 + r)
on doubling_time(rate: Float) {
return log(2.0) / log(1.0 + rate)
}
}
cell test CompoundTests {
rules {
// 1000 at 5% for 2 years = 1102.50
assert abs(future_value(1000.0, 0.05, 2) - 1102.5) < 0.001
// monthly compounding beats annual
assert future_value_n(1000.0, 0.12, 12, 1) > future_value(1000.0, 0.12, 1)
// continuous is the upper bound
assert abs(continuous(1000.0, 0.10, 1.0) - 1105.170918) < 0.001
// at 7%, doubling takes ~10.24 years
assert abs(doubling_time(0.07) - 10.244768) < 0.001
}
}What the toolchain says
$ soma check compound_interest.cell
✓ All checks passed.$ soma test compound_interest.cell
test CompoundTests ...
✓ assert abs(future_value(1000.0, 0.05, 2) - 1102.5) < 0.001
✓ assert future_value_n(1000.0, 0.12, 12, 1) > future_value(1000.0, 0.12, 1)
✓ assert abs(continuous(1000.0, 0.10, 1.0) - 1105.170918) < 0.001
✓ assert abs(doubling_time(0.07) - 10.244768) < 0.001
4 tests: 4 passed, 0 failed$ soma verify compound_interest.cell
Verifying compound_interest.cell...
No state machine in this program: nothing to prove beyond `soma check` (invariants and lifecycles are what verify proves).
VERIFY OK (vacuous — no state machine)