Skip to main content

finance_solution/tvm/
present_value.rs

1//! **Present value calculations.** Given a final amount, a number of periods such as years, and fixed
2//! or varying interest rates, what is the current value?
3//!
4//! For most common usages, we recommend the [`present_value_solution`](./fn.present_value_solution.html) function to provide a better debugging experience and additional features.
5//!
6//! If you have a more complicated use case which has varying rates per period, use the [`present_value_schedule_solution`](./fn.present_value_schedule_solution.html) function.
7//!
8// ! If you need to calculate the future value given a present value, a number of periods, and one
9// ! or more rates use [`future_value`] or related functions.
10// !
11// ! If you need to calculate a fixed rate given a present value, future value, and number of periods
12// ! use [`rate`] or related functions.
13// !
14// ! If you need to calculate the number of periods given a fixed rate and a present and future value
15// ! use [`periods`] or related functions.
16//! # Formulas
17//!
18//! ## Simple Compounding
19//!
20//! With simple compound interest, the present value is calculated with:
21//!
22//! > <img src="http://i.upmath.me/svg/present%5C_value%20%3D%20%7Bfuture%5C_value%20%5Cover%20(1%2Brate)%5E%7Bperiods%7D%7D" />
23//!
24//! Or using some more usual variable names:
25//!
26//! > <img src="http://i.upmath.me/svg/pv%20%3D%20%7Bfv%20%5Cover%20(1%2Br)%5En%7D" />
27//!
28//! `n` is often used for the number of periods, though it may be `t` for time if each period is
29//! assumed to be one year as in continuous compounding. `r` is the periodic rate, though this may
30//! appear as `i` for interest.
31//!
32//! Throughout this crate we use `pv` for present value and `fv` for future value. You may see these
33//! values called `P` for principal in some references.
34//!
35//! Within the [TvmSolution](././tvm_simple/struct.TvmSolution.html) struct we record the formula used for the particular calculation
36//! using both concrete values and symbols. For example if we calculated the present value of an
37//! investment that grows by 1.5% per month for 48 months using simple compounding and reaches a
38//! future value of $50,000 the solution struct would contain these fields:
39//! ```text
40//! formula: "24468.0848 = 50000.0000 / (1.015000 ^ 48)",
41//! symbolic_formula: "pv = fv / (1 + r)^n",
42//! ```
43//!
44//! ## Continuous Compounding
45//!
46//! With continuous compounding the formula is:
47//!
48//! > <img src="http://i.upmath.me/svg/present%5C_value%20%3D%20%7Bfuture%5C_value%20%5Cover%20e%5E%7Brate%20%5Ctimes%20periods%7D%7D" />
49//!
50//! or:
51//!
52//! > <img src="http:i.upmath.me/svg/pv%20%3D%20%7Bfv%20%5Cover%20e%5E%7Br%20%5Ctimes%20n%7D%7D" />
53//!
54//! With continuous compounding the period is assumed to be years and `t` (time) is often used as
55//! the variable name. Within this crate we stick with `n` for the number of periods so that it's
56//! easier to compare formulas when they're printed as simple text as part of the [TvmSolution](./struct.TvmSolution.html)
57//! struct. Taking the example above but switching to continuous compounding the struct would
58//! contain these fields:
59//! ```text
60//! formula: "24337.6128 = 50000.0000 / 2.718282^(0.015000 * 48)",
61//! symbolic_formula: "pv = fv / e^(rt)",
62//! ```
63use log::warn;
64
65use super::tvm::*;
66
67/// Returns the current value of a future amount using a fixed rate.
68///
69/// Related functions:
70/// * To calculate a present value with a fixed rate and return a struct that shows the formula and
71/// optionally produces the the period-by-period values use [`present_value_solution`](./fn.present_value_solution.html).
72/// * To calculate the present value if the rates vary by period use [`present_value_schedule`](./fn.present_value_schedule.html)
73/// or [`present_value_schedule_solution`](./fn.present_value_schedule_solution.html).
74///
75/// See the [present_value](./index.html) module page for the formulas.
76///
77/// # Arguments
78/// * `rate` - The rate at which the investment grows or shrinks per period,
79/// expressed as a floating point number. For instance 0.05 would mean 5% growth. Often appears as
80/// `r` or `i` in formulas.
81/// * `periods` - The number of periods such as quarters or years. Often appears as `n` or `t`.
82/// * `future_value` - The final value of the investment.
83/// * `continuous_compounding` - True for continuous compounding, false for simple compounding.
84///
85/// # Errors
86/// The call returns [`FinanceError`] if `rate` is less than -1.0 as this would mean the investment is
87/// losing more than its full value every period. It returns an error also if the future value is zero as
88/// in this case there's no way to determine the present value.
89///
90/// # Examples
91/// Investment that grows month by month.
92/// ```
93/// use finance_solution::*;
94///
95/// // The investment will grow by 1.1% per month.
96/// let rate = 0.011;
97///
98/// // The investment will grow for 12 months.
99/// let periods = 12;
100///
101/// // The final value will be $50,000.
102/// let future_value = 50_000;
103///
104/// let continuous_compounding = false;
105///
106/// // Find the current value.
107/// let present_value = present_value(rate, periods, future_value as f64, continuous_compounding).unwrap();
108/// dbg!(&present_value);
109///
110/// // Confirm that the present value is correct to four decimal places (one hundredth of a cent).
111/// assert_rounded_4(-43_848.6409, present_value);
112/// ```
113/// Error case: rate less than −100% per period is outside the domain.
114/// ```
115/// # use finance_solution::{present_value, FinanceError};
116/// let rate = -1.05;
117/// let periods = 6;
118/// let future_value = -10_000.75;
119/// let err = present_value(rate, periods, future_value, false).unwrap_err();
120/// assert!(matches!(err, FinanceError::InvalidRate { .. }));
121/// ```
122/// # Errors
123/// Returns [`FinanceError::InvalidRate`] if `rate < -1.0`, [`FinanceError::ZeroValue`] if
124/// `future_value` is zero/subnormal, or [`FinanceError::NonFinite`] for non-finite values.
125///
126/// # Examples
127/// ```
128/// use finance_solution::{present_value, FinanceError, FinanceResult};
129///
130/// assert!(present_value(0.05, 10, 1000.0, false).is_ok());
131///
132/// match present_value(-1.5, 10, 1000.0, false) {
133///     Err(FinanceError::InvalidRate { rate }) => assert_eq!(rate, -1.5),
134///     other => panic!("expected InvalidRate, got {other:?}"),
135/// }
136///
137/// match present_value(0.05, 10, 0.0, false) {
138///     Err(FinanceError::ZeroValue { field }) => assert_eq!(field, "future_value"),
139///     other => panic!("expected ZeroValue, got {other:?}"),
140/// }
141///
142/// fn discount(fv: f64) -> FinanceResult<f64> {
143///     present_value(0.06, 8, fv, false)
144/// }
145/// match discount(50_000.0) {
146///     Ok(pv) => assert!(pv < 0.0),
147///     Err(e) => panic!("{e}"),
148/// }
149/// ```
150pub fn present_value<T, C>(
151    rate: f64,
152    periods: u32,
153    future_value: T,
154    compounding: C,
155) -> crate::FinanceResult<f64>
156where
157    T: Into<f64> + Copy,
158    C: Into<crate::Compounding>,
159{
160    present_value_internal(
161        rate,
162        periods as f64,
163        future_value.into(),
164        compounding.into().is_continuous(),
165    )
166}
167
168/// Calculates the current value of a future amount using a fixed rate and returns a struct
169/// with the inputs and the calculated value. This is used for keeping track of a collection of
170/// financial scenarios so that they can be examined later.
171///
172/// See the [present_value](./index.html) module page for the formulas.
173///
174/// Related functions:
175/// * For simply calculating a single present value using a fixed rate use [`present_value`](./fn.present_value.html).
176/// * To calculate the present value if the rates vary by period use [`present_value_schedule`](./fn.present_value_schedule.html)
177/// or [`present_value_schedule_solution`](./fn.present_value_schedule_solution.html).
178///
179/// # Arguments
180/// * `rate` - The rate at which the investment grows or shrinks per period,
181/// expressed as a floating point number. For instance 0.05 would mean 5% growth. Often appears as
182/// `r` or `i` in formulas.
183/// * `periods` - The number of periods such as quarters or years. Often appears as `n` or `t`.
184/// * `future_value` - The final value of the investment.
185/// * `continuous_compounding` - True for continuous compounding, false for simple compounding.
186///
187/// # Errors
188/// The call returns [`FinanceError`] if `rate` is less than -1.0 as this would mean the investment is
189/// losing more than its full value every period. It returns an error also if the future value is zero as
190/// in this case there's no way to determine the present value.
191///
192/// # Examples
193/// Calculate a present value and examine the period-by-period values.
194/// ```
195/// use finance_solution::*;
196///
197/// // The rate is 8.45% per year.
198/// let rate = 0.0845;
199///
200/// // The investment will grow for six years.
201/// let periods = 6;
202///
203/// // The final value is $50,000.
204/// let future_value = 50_000;
205///
206/// let continuous_compounding = false;
207///
208/// // Calculate the present value and create a struct with the input values and
209/// // the formula used.
210/// let solution = present_value_solution(rate, periods, future_value, continuous_compounding).unwrap();
211/// dbg!(&solution);
212///
213/// let present_value = solution.present_value();
214/// assert_rounded_4(present_value, -30_732.1303);
215///
216/// // Examine the formulas.
217/// let formula = solution.formula();
218/// dbg!(&formula);
219/// assert_eq!(formula, "-30732.1303 = -50000.0000 / (1.084500 ^ 6)");
220/// let symbolic_formula = solution.symbolic_formula();
221/// dbg!(&symbolic_formula);
222/// assert_eq!("pv = -fv / (1 + r)^n", symbolic_formula);
223///
224/// // Calculate the amount at the end of each period.
225/// let series = solution.series();
226/// dbg!(&series);
227/// ```
228/// Build a collection of present value calculations where the future value and periodic rate are
229/// fixed but the number of periods varies, then filter the results.
230/// ```
231/// // The rate is 0.9% per month.
232/// # use finance_solution::*;
233/// let rate = 0.009;
234///
235/// // The final value is $100,000.
236/// let future_value = 100_000;
237///
238/// let continuous_compounding = false;
239///
240/// // We'll keep a collection of the calculated present values along with their inputs.
241/// let mut scenarios = vec![];
242///
243/// // Calculate the present value for terms ranging from 1 to 36 months.
244/// for periods in 1..=36 {
245///     // Calculate the future value for this number of months and add the details to the
246///     // collection.
247///     scenarios.push(present_value_solution(rate, periods, future_value, continuous_compounding).unwrap());
248/// }
249/// dbg!(&scenarios);
250/// assert_eq!(36, scenarios.len());
251///
252/// // Keep only the scenarios where the present value (which is negative) is greater than or
253/// // than or equal to -$80,000.
254/// scenarios.retain(|x| x.present_value() >= -80_000.00);
255/// dbg!(&scenarios);
256/// assert_eq!(12, scenarios.len());
257///
258/// // Find the range of months for the remaining scenarios.
259/// let min_months = scenarios.iter().map(|x| x.periods()).min().unwrap();
260/// let max_months = scenarios.iter().map(|x| x.periods()).max().unwrap();
261/// dbg!(min_months, max_months);
262/// assert_eq!(25, min_months);
263/// assert_eq!(36, max_months);
264///
265/// // Check the formulas for the first of the remaining scenarios.
266/// let formula = scenarios[0].formula();
267/// dbg!(&formula);
268/// assert_eq!("-79932.0303 = -100000.0000 / (1.009000 ^ 25)", formula);
269/// let symbolic_formula = scenarios[0].symbolic_formula();
270/// dbg!(&symbolic_formula);
271/// assert_eq!("pv = -fv / (1 + r)^n", symbolic_formula);
272///
273/// ```
274/// Error case: rate less than −100% per period is outside the domain.
275/// ```
276/// # use finance_solution::{present_value_solution, FinanceError};
277/// let rate = -1.11;
278/// let periods = 12;
279/// let future_value = 100_000.85;
280/// let err = present_value_solution(rate, periods, future_value, false).unwrap_err();
281/// assert!(matches!(err, FinanceError::InvalidRate { .. }));
282/// ```
283pub fn present_value_solution<T, C>(
284    rate: f64,
285    periods: u32,
286    future_value: T,
287    compounding: C,
288) -> crate::FinanceResult<TvmSolution>
289where
290    T: Into<f64> + Copy,
291    C: Into<crate::Compounding>,
292{
293    present_value_solution_internal(
294        rate,
295        periods as f64,
296        future_value.into(),
297        compounding.into().is_continuous(),
298    )
299}
300
301/// Calculates a present value based on rates that change for each period.
302///
303/// Related functions:
304/// * To calculate the present value with varying rates and return a struct that can produce the
305/// period-by-period values use [`present_value_schedule_solution`](./fn.present_value_schedule_solution.html).
306/// * If there is a single fixed rate use [present_value](./fn.present_value.html) or [present_value_solution](./fn.present_value_solution.html).
307///
308/// # Arguments
309/// * `rates` - A collection of rates, one for each period.
310/// * `future_value` - The ending value of the investment.
311///
312/// # Errors
313/// The call returns [`FinanceError`] if any of the rates is less than -1.0 as this would mean the investment is
314/// losing more than its full value every period. It returns an error also if the future value is zero as
315/// in this case there's no way to determine the present value.
316///
317/// # Examples
318/// Calculate the present value of an investment whose rates vary by year.
319/// ```
320/// // The annual rate varies from -3.4% to 12.9%.
321/// let rates = [0.04, -0.034, 0.0122, 0.129, 8.5];
322///
323/// // The value of the investment after applying all of these periodic rates
324/// // will be $30_000.
325/// let future_value = 30_000.00;
326///
327/// // Calculate the present value.
328/// let present_value = finance_solution::present_value_schedule(&rates, future_value).unwrap();
329/// dbg!(&present_value);
330/// ```
331pub fn present_value_schedule<T>(rates: &[f64], future_value: T) -> crate::FinanceResult<f64>
332where
333    T: Into<f64> + Copy,
334{
335    let future_value = future_value.into();
336    crate::util::error::require_rates(rates)?;
337    crate::util::error::require_money("future_value", future_value)?;
338    if !future_value.is_normal() {
339        return Err(crate::FinanceError::ZeroValue {
340            field: "future_value",
341        });
342    }
343    let periods = rates.len();
344    let mut present_value = -future_value;
345    for i in (0..periods).rev() {
346        present_value /= 1.0 + rates[i];
347    }
348    if present_value.is_finite() {
349        Ok(present_value)
350    } else {
351        Err(crate::FinanceError::NonFinite {
352            field: "present_value",
353            value: present_value,
354        })
355    }
356}
357
358/// Calculates a present value based on rates that change for each period and returns a struct
359/// with the inputs and the calculated value.
360///
361/// Related functions:
362/// * To calculate the present value as a single number if the rates vary by period use
363/// [present_value_schedule](./fn.present_value_schedule.html).
364/// * If there is a single fixed rate use [present_value](./fn.present_value.html) or
365/// [present_value_solution](./fn.present_value_solution.html).
366///
367/// # Arguments
368/// * `rates` - A collection of rates, one for each period.
369/// * `future_value` - The ending value of the investment.
370///
371/// # Errors
372/// The call returns [`FinanceError`] if any of the rates is less than -1.0 as this would mean the investment is
373/// losing more than its full value every period. It returns an error also if the future value is zero as
374/// in this case there's no way to determine the present value.
375///
376/// # Examples
377/// Calculate the value of an investment whose rates vary by year, then view only those periods
378/// where the rate is negative.
379/// ```
380/// use finance_solution::*;
381///
382/// // The quarterly rate varies from -0.5% to 4%.
383/// let rates = [0.04, 0.008, 0.0122, -0.005];
384///
385/// // The value of the investment after applying all of these periodic rates
386/// // will be $25_000.
387/// let future_value = 25_000.00;
388///
389/// // Calculate the present value and keep track of the inputs and the formula
390/// // in a struct.
391/// let solution = present_value_schedule_solution(&rates, future_value).unwrap();
392/// dbg!(&solution);
393///
394/// let present_value = solution.present_value();
395/// assert_rounded_4(present_value, -23_678.6383);
396///
397/// // Calculate the value for each period.
398/// let series = solution.series();
399/// dbg!(&series);
400/// ```
401pub fn present_value_schedule_solution<T>(
402    rates: &[f64],
403    future_value: T,
404) -> crate::FinanceResult<TvmScheduleSolution>
405where
406    T: Into<f64> + Copy,
407{
408    let future_value = future_value.into();
409    let present_value = present_value_schedule(rates, future_value)?;
410    Ok(TvmScheduleSolution::new(
411        TvmVariable::PresentValue,
412        rates,
413        present_value,
414        future_value,
415    ))
416}
417
418pub(crate) fn present_value_internal(
419    rate: f64,
420    periods: f64,
421    future_value: f64,
422    continuous_compounding: bool,
423) -> crate::FinanceResult<f64> {
424    crate::util::error::require_rate(rate)?;
425    crate::util::error::require_money("future_value", future_value)?;
426    if !future_value.is_normal() {
427        return Err(crate::FinanceError::ZeroValue {
428            field: "future_value",
429        });
430    }
431    if rate.abs() > 1.0 {
432        warn!(
433            "You provided a periodic rate ({}) greater than 1. Are you sure you expect a {}% return?",
434            rate,
435            rate * 100.0
436        );
437    }
438    let present_value = if continuous_compounding {
439        -future_value / std::f64::consts::E.powf(rate * periods)
440    } else {
441        -future_value / (1.0 + rate).powf(periods)
442    };
443    if present_value.is_finite() {
444        Ok(present_value)
445    } else {
446        Err(crate::FinanceError::NonFinite {
447            field: "present_value",
448            value: present_value,
449        })
450    }
451}
452
453pub(crate) fn present_value_solution_internal(
454    rate: f64,
455    periods: f64,
456    future_value: f64,
457    continuous_compounding: bool,
458) -> crate::FinanceResult<TvmSolution> {
459    let present_value =
460        present_value_internal(rate, periods, future_value, continuous_compounding)?;
461    let rate_multiplier = 1.0 + rate;
462    let (formula, symbolic_formula) = if continuous_compounding {
463        let formula = format!(
464            "{:.4} = {:.4} / {:.6}^({:.6} * {})",
465            present_value,
466            -future_value,
467            std::f64::consts::E,
468            rate,
469            periods
470        );
471        let symbolic_formula = "pv = -fv / e^(rt)";
472        (formula, symbolic_formula)
473    } else {
474        let formula = format!(
475            "{:.4} = {:.4} / ({:.6} ^ {})",
476            present_value, -future_value, rate_multiplier, periods
477        );
478        let symbolic_formula = "pv = -fv / (1 + r)^n";
479        (formula, symbolic_formula)
480    };
481    Ok(TvmSolution::new_fractional_periods(
482        TvmVariable::PresentValue,
483        continuous_compounding,
484        rate,
485        periods,
486        present_value,
487        future_value,
488        &formula,
489        symbolic_formula,
490    ))
491}
492
493#[cfg(test)]
494mod tests {
495    use super::*;
496    use crate::*;
497
498    #[test]
499    fn test_present_value_schedule() {
500        let rates = [0.04, 0.07, -0.12, -0.03, 0.11];
501        let future_value = 100_000.25;
502
503        let present_value = present_value_schedule(&rates, future_value).unwrap();
504        assert_rounded_4(-94843.2841, present_value);
505
506        let solution = present_value_schedule_solution(&rates, future_value).unwrap();
507        assert_rounded_4(100000.2500, solution.future_value());
508        assert_rounded_4(-94843.2841, solution.present_value());
509
510        let series = solution.series();
511        assert_eq!(6, series.len());
512
513        let period = &series[0];
514        assert_eq!(0, period.period());
515        assert_rounded_6(0.0, period.rate());
516        assert_rounded_4(-present_value, period.value());
517
518        let period = &series[1];
519        assert_eq!(1, period.period());
520        assert_rounded_6(0.04, period.rate());
521        assert_rounded_4(98_637.0154, period.value());
522
523        let period = &series[2];
524        assert_eq!(2, period.period());
525        assert_rounded_6(0.07, period.rate());
526        assert_rounded_4(105_541.6065, period.value());
527
528        let period = &series[3];
529        assert_eq!(3, period.period());
530        assert_rounded_6(-0.12, period.rate());
531        assert_rounded_4(92_876.6137, period.value());
532
533        let period = &series[4];
534        assert_eq!(4, period.period());
535        assert_rounded_6(-0.03, period.rate());
536        assert_rounded_4(90_090.3153, period.value());
537
538        let period = &series[5];
539        assert_eq!(5, period.period());
540        assert_rounded_6(0.11, period.rate());
541        assert_rounded_4(100_000.2500, period.value());
542    }
543
544    /*
545    macro_rules! compare_to_excel {
546        ( $r:expr, $n:expr, $fv:expr, $pv_excel:expr, $pv_manual_simple:expr, $pv_manual_cont:expr ) => {
547            println!("$r = {}, $n = {}, $fv = {}, $pv_excel: {}, $pv_manual_simple = {}, $pv_manual_cont = {}", $r, $n, $fv, $pv_excel, $pv_manual_simple, $pv_manual_cont);
548            assert_approx_equal!($pv_excel, $pv_manual_simple);
549
550            let pv_calc_simple = present_value($r, $n, $fv, false).unwrap();
551            println!("pv_calc_simple = {}", pv_calc_simple);
552            assert_approx_equal!($pv_excel, pv_calc_simple);
553
554            let pv_calc_cont = present_value($r, $n, $fv, true).unwrap();
555            println!("pv_calc_cont = {}", pv_calc_cont);
556            assert_approx_equal!($pv_manual_cont, pv_calc_cont);
557
558            let ratio = pv_calc_cont / pv_calc_simple;
559            println!("ratio = {}", ratio);
560            assert!(ratio > 0.0);
561            assert!(ratio <= 1.0);
562        }
563    }
564    */
565
566    fn compare_to_excel(
567        test_case: usize,
568        r: f64,
569        n: u32,
570        fv: f64,
571        pv_excel: f64,
572        pv_manual_simple: f64,
573        pv_manual_cont: f64,
574    ) {
575        let display = false;
576
577        if display {
578            println!("test_case = {}, r = {}, n = {}, fv = {}, pv_excel: {}, pv_manual_simple = {}, pv_manual_cont = {}", test_case, r, n, fv, pv_excel, pv_manual_simple, pv_manual_cont)
579        };
580        assert_approx_equal!(pv_excel, pv_manual_simple);
581
582        let pv_calc_simple = present_value(r, n, fv, false).unwrap();
583        if display {
584            println!("pv_calc_simple = {}", pv_calc_simple)
585        };
586        assert_approx_equal!(pv_excel, pv_calc_simple);
587
588        let pv_calc_cont = present_value(r, n, fv, true).unwrap();
589        if display {
590            println!("pv_calc_cont = {}", pv_calc_cont)
591        };
592        assert_approx_equal!(pv_manual_cont, pv_calc_cont);
593
594        let ratio = pv_calc_cont / pv_calc_simple;
595        if display {
596            println!("ratio = {}", ratio)
597        };
598        assert!(ratio >= 0.0);
599        assert!(ratio <= 1.0);
600
601        // Solution with simple compounding.
602        let solution = present_value_solution(r, n, fv, false).unwrap();
603        if display {
604            dbg!(&solution);
605        }
606        solution.invariant();
607        assert!(solution.calculated_field().is_present_value());
608        assert_eq!(false, solution.continuous_compounding());
609        assert_approx_equal!(r, solution.rate());
610        assert_eq!(n, solution.periods());
611        assert_approx_equal!(n as f64, solution.fractional_periods());
612        assert_approx_equal!(pv_excel, solution.present_value());
613        assert_approx_equal!(fv, solution.future_value());
614
615        // Solution with continuous compounding.
616        let solution = present_value_solution(r, n, fv, true).unwrap();
617        if display {
618            dbg!(&solution);
619        }
620        solution.invariant();
621        assert!(solution.calculated_field().is_present_value());
622        assert!(solution.continuous_compounding());
623        assert_approx_equal!(r, solution.rate());
624        assert_eq!(n, solution.periods());
625        assert_approx_equal!(n as f64, solution.fractional_periods());
626        assert_approx_equal!(pv_manual_cont, solution.present_value());
627        assert_approx_equal!(fv, solution.future_value());
628
629        let rates = initialized_vector(n as usize, r);
630
631        // Schedule solution.
632        let solution = present_value_schedule_solution(&rates, fv).unwrap();
633        if display {
634            dbg!(&solution);
635        }
636        solution.invariant();
637        assert!(solution.calculated_field().is_present_value());
638        assert_eq!(n, solution.periods());
639        assert_approx_equal!(pv_excel, solution.present_value());
640        assert_approx_equal!(fv, solution.future_value());
641    }
642
643    #[test]
644    fn test_present_value_against_excel() {
645        compare_to_excel(
646            1,
647            0.01f64,
648            90,
649            1f64,
650            -0.408391185151344f64,
651            -0.408391185151344f64,
652            -0.406569659740599f64,
653        );
654        compare_to_excel(
655            2,
656            -0.01f64,
657            85,
658            -1.5f64,
659            3.52451788132823f64,
660            3.52451788132823f64,
661            3.50947027788899f64,
662        );
663        compare_to_excel(3, 0f64, 80, 2.25f64, -2.25f64, -2.25f64, -2.25f64);
664        compare_to_excel(
665            4,
666            0.05f64,
667            75,
668            -3.375f64,
669            0.0869113201859512f64,
670            0.0869113201859512f64,
671            0.0793723922640307f64,
672        );
673        compare_to_excel(
674            5,
675            -0.05f64,
676            70,
677            5.0625f64,
678            -183.53236712846f64,
679            -183.53236712846f64,
680            -167.64697554088f64,
681        );
682        compare_to_excel(
683            6,
684            0.01f64,
685            65,
686            -7.59375f64,
687            3.97710447262579f64,
688            3.97710447262579f64,
689            3.96428511727897f64,
690        );
691        compare_to_excel(
692            7,
693            -0.01f64,
694            60,
695            11.390625f64,
696            -20.8178501685176f64,
697            -20.8178501685176f64,
698            -20.7550719606981f64,
699        );
700        compare_to_excel(
701            8,
702            0f64,
703            55,
704            -17.0859375f64,
705            17.0859375f64,
706            17.0859375f64,
707            17.0859375f64,
708        );
709        compare_to_excel(
710            9,
711            0.05f64,
712            50,
713            25.62890625f64,
714            -2.23493614322574f64,
715            -2.23493614322574f64,
716            -2.10374873426328f64,
717        );
718        compare_to_excel(
719            10,
720            -0.05f64,
721            45,
722            -38.443359375f64,
723            386.597546504632f64,
724            386.597546504632f64,
725            364.740438412197f64,
726        );
727        compare_to_excel(
728            11,
729            0.01f64,
730            40,
731            57.6650390625f64,
732            -38.7309044888379f64,
733            -38.7309044888379f64,
734            -38.6540316390219f64,
735        );
736        compare_to_excel(
737            12,
738            -0.01f64,
739            35,
740            -86.49755859375f64,
741            122.962317182378f64,
742            122.962317182378f64,
743            122.745878432934f64,
744        );
745        compare_to_excel(
746            13,
747            0f64,
748            30,
749            129.746337890625f64,
750            -129.746337890625f64,
751            -129.746337890625f64,
752            -129.746337890625f64,
753        );
754        compare_to_excel(
755            14,
756            0.05f64,
757            25,
758            -194.619506835937f64,
759            57.4716797951039f64,
760            57.4716797951039f64,
761            55.7594222710607f64,
762        );
763        compare_to_excel(
764            15,
765            -0.05f64,
766            20,
767            291.929260253906f64,
768            -814.33953749853f64,
769            -814.33953749853f64,
770            -793.546003343685f64,
771        );
772        compare_to_excel(
773            16,
774            0.01f64,
775            15,
776            -437.893890380859f64,
777            377.179672510109f64,
778            377.179672510109f64,
779            376.898764278606f64,
780        );
781        compare_to_excel(
782            17,
783            -0.01f64,
784            12,
785            656.840835571289f64,
786            -741.033445550103f64,
787            -741.033445550103f64,
788            -740.585974095395f64,
789        );
790        compare_to_excel(
791            18,
792            0f64,
793            10,
794            -985.261253356933f64,
795            985.261253356933f64,
796            985.261253356933f64,
797            985.261253356933f64,
798        );
799        compare_to_excel(
800            19,
801            0.05f64,
802            7,
803            1477.8918800354f64,
804            -1050.31016709206f64,
805            -1050.31016709206f64,
806            -1041.45280575294f64,
807        );
808        compare_to_excel(
809            20,
810            -0.05f64,
811            5,
812            -2216.8378200531f64,
813            2864.94240503709f64,
814            2864.94240503709f64,
815            2846.47610562283f64,
816        );
817        compare_to_excel(
818            21,
819            0.01f64,
820            4,
821            3325.25673007965f64,
822            -3195.50635796575f64,
823            -3195.50635796575f64,
824            -3194.87154873072f64,
825        );
826        compare_to_excel(
827            22,
828            -0.01f64,
829            3,
830            -4987.88509511947f64,
831            5140.56501667989f64,
832            5140.56501667989f64,
833            5139.78881110503f64,
834        );
835        compare_to_excel(
836            23,
837            0f64,
838            2,
839            7481.82764267921f64,
840            -7481.82764267921f64,
841            -7481.82764267921f64,
842            -7481.82764267921f64,
843        );
844        compare_to_excel(
845            24,
846            0.05f64,
847            1,
848            -11222.7414640188f64,
849            10688.3252038274f64,
850            10688.3252038274f64,
851            10675.4019041389f64,
852        );
853        compare_to_excel(
854            25,
855            -0.05f64,
856            0,
857            16834.1121960282f64,
858            -16834.1121960282f64,
859            -16834.1121960282f64,
860            -16834.1121960282f64,
861        );
862    }
863}