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}