Skip to main content

finance_solution/tvm/
future_value.rs

1//! **Future value calculations.** Given an initial investment amount, a number of periods such as
2//! periods, and fixed or varying interest rates, what is the value of the investment at the end?
3//!
4//! For most common usages, we recommend the [future_value_solution](./fn.future_value_solution.html) function, which provides a better debugging experience and additional features.
5//!
6//! For more complex scenarios, which involve varying rates in each period, we recommend the [future_value_schedule_solution](./fn.future_value_schedule_solution.html) function.
7//!
8//! To simply return an f64 value of the future value answer, use the [future_value](./fn.future_value.html) function.
9//!
10// ! If you need to calculate the present value given a future value, a number of periods, and one
11// ! or more rates use [`present_value`] or related functions.
12// !
13// ! If you need to calculate a fixed rate given a present value, future value, and number of periods
14// ! use [`rate`] or related functions.
15// !
16// ! If you need to calculate the number of periods given a fixed rate and a present and future value
17// ! use [`periods`] or related functions.
18//!
19//! ## Example
20//!
21//! ```
22//! let (rate, periods, present_value, continuous_compounding) = (0.034, 10, 1_000, false);
23//! let fv = finance_solution::future_value_solution(rate, periods, present_value, continuous_compounding).unwrap();
24//! dbg!(fv);
25//! ```
26//! Outputs to terminal:
27//! ```text
28//! {
29//!     calculated_field: FutureValue,
30//!     continuous_compounding: false,
31//!     rate: 0.034,
32//!     periods: 10,
33//!     fractional_periods: 10.0,
34//!     present_value: 1000.0,
35//!     future_value: 1397.0288910795477,
36//!     formula: "1397.0289 = 1000.0000 * (1.034000 ^ 10)",
37//!     symbolic_formula: "fv = pv * (1 + r)^n",
38//! }
39//! ```
40//! # Formulas
41//!
42//! ## Simple Compounding
43//!
44//! With simple compound interest, the future value is calculated with:
45//!
46//! > <img src="http://i.upmath.me/svg/future%5C_value%20%3D%20present%5C_value%20%5Ctimes%20(1%2Brate)%5E%7Bperiods%7D" />
47//!
48//! Or with some more commonly-used variable names:
49//!
50//! > <img src="http://i.upmath.me/svg/fv%20%3D%20pv%20%5Ctimes%20(1%2Br)%5En" />
51//!
52//! `n` is often used for the number of periods, though it may be `t` for time if each period is
53//! assumed to be one year as in continuous compounding. `r` is the periodic rate, though this may
54//! appear as `i` for interest.
55//!
56//! Throughout this crate we use `pv` for present value and `fv` for future value. You may see these
57//! values called `P` for principal in some references.
58//!
59//! Within the [TvmSolution](./struct.TvmSolution.html) struct we record the formula used for the particular calculation
60//! using both concrete values and symbols. For example with $1,000 growing at 3.5% per period for
61//! 12 periods using simple compounding the struct contains these fields:
62//! ```text
63//! formula: "1511.0687 = 1000.0000 * (1.035000 ^ 12)",
64//! symbolic_formula: "fv = pv * (1 + r)^n",
65//! ```
66//!
67//! ## Continuous Compounding
68//!
69//! With continuous compounding the formula is:
70//!
71//! > <img src="http://i.upmath.me/svg/future%5C_value%20%3D%20%7Bpresent%5C_value%20%5Ctimes%20e%5E%7Brate%20%5Ctimes%20periods%7D" />
72//!
73//! or:
74//!
75//! > <img src="http://i.upmath.me/svg/fv%20%3D%20pv%20%5Ctimes%20e%5E%7Br%20%5Ctimes%20n%7D" />
76//!
77//! With continuous compounding the period is assumed to be years and `t` (time) is often used as
78//! the variable name. Within this crate we stick with `n` for the number of periods so that it's
79//! easier to compare formulas when they're printed as simple text as part of the [TvmSolution](./struct.TvmSolution.html)
80//! struct. For example with $1,000 growing at 3.5% per period for 12 periods using continuous
81//! compounding the struct contains these fields:
82//! ```text
83//! formula: "1521.9616 = 1000.0000 * 2.718282^(0.035000 * 12)",
84//! symbolic_formula: "fv = pv * e^(rt)",
85//! ```
86//! This is the same as the example in the previous section except that it uses continuous
87//! compounding.
88use log::warn;
89
90use super::tvm::*;
91
92#[allow(unused_imports)]
93use crate::{periods::*, present_value::*, rate::*};
94
95/// Returns the value of an investment after it has grown or shrunk over time, using a fixed rate.
96///
97/// See the [future_value](./index.html) module page for the formulas.
98///
99/// Related functions:
100/// * To calculate a future value with a fixed rate and return a struct that shows the formula and
101/// optionally produces the the period-by-period values use [`future_value_solution`].
102/// * To calculate the future value if the rates vary by period use [`future_value_schedule`] or
103/// [`future_value_schedule_solution`].
104///
105/// # Arguments
106/// * `rate` - The rate at which the investment grows or shrinks per period, expressed as a
107/// floating point number. For instance 0.05 would mean 5% growth. Often appears as `r` or `i` in
108/// formulas.
109/// * `periods` - The number of periods such as quarters or periods. Often appears as `n` or `t`.
110/// * `present_value` - The starting value of the investment. May appear as `pv` in formulas, or `C`
111/// for cash flow or `P` for principal.
112/// * `continuous_compounding` - True for continuous compounding, false for simple compounding.
113///
114/// # Errors
115/// The call returns [`FinanceError`] if `rate` is less than -1.0 as this would mean the investment is
116/// losing more than its full value every period.
117///
118/// # Examples
119/// Investment that grows quarter by quarter.
120/// ```
121/// use finance_solution::*;
122///
123/// // The investment grows by 3.4% per quarter.
124/// let rate = 0.034;
125///
126/// // The investment will grow for 5 quarters.
127/// let periods = 5;
128///
129/// // The initial investment is $250,000.
130/// let present_value = -250_000;
131///
132/// let continuous_compounding = false;
133///
134/// let future_value = future_value(rate, periods, present_value, continuous_compounding).unwrap();
135/// // Confirm that the future value is correct to four decimal places (one
136/// // hundredth of a cent).
137/// assert_rounded_4(295_489.9418, future_value);
138/// ```
139/// Investment that loses money each year.
140/// ```
141/// # use finance_solution::*;
142/// // The investment loses 5% per year.
143/// let rate = -0.05;
144///
145/// // The investment will shrink for 6 periods.
146/// let periods = 6;
147///
148/// // The initial investment is $10,000.75.
149/// let present_value = -10_000.75;
150///
151/// let continuous_compounding = false;
152///
153/// let future_value = future_value(rate, periods, present_value, continuous_compounding).unwrap();
154/// // Confirm that the future value is correct to the penny.
155/// assert_rounded_2(7351.47, future_value);
156/// ```
157/// Error case: rate less than −100% per period is outside the domain.
158/// ```
159/// # use finance_solution::{future_value, FinanceError};
160/// let err = future_value(-1.05, 6, 10_000.75, false).unwrap_err();
161/// assert!(matches!(err, FinanceError::InvalidRate { .. }));
162/// ```
163///
164/// # Errors
165/// Returns [`FinanceError::InvalidRate`] if `rate < -1.0`, or [`FinanceError::NonFinite`]
166/// if inputs or the computed result are not finite.
167///
168/// # Examples
169/// ```
170/// use finance_solution::{future_value, FinanceError, FinanceResult};
171///
172/// match future_value(0.05, 10, -1_000.0, false) {
173///     Ok(fv) => assert!(fv > 0.0),
174///     Err(FinanceError::InvalidRate { rate }) => panic!("unexpected bad rate {rate}"),
175///     Err(e) => panic!("{e}"),
176/// }
177///
178/// assert!(matches!(
179///     future_value(-1.5, 10, 1000.0, false),
180///     Err(FinanceError::InvalidRate { .. })
181/// ));
182///
183/// fn project(pv: f64, years: u32) -> FinanceResult<f64> {
184///     future_value(0.07, years, pv, false)
185/// }
186/// assert!(project(-5_000.0, 5).is_ok());
187/// ```
188pub fn future_value<T, C>(
189    rate: f64,
190    periods: u32,
191    present_value: T,
192    compounding: C,
193) -> crate::FinanceResult<f64>
194where
195    T: Into<f64> + Copy,
196    C: Into<crate::Compounding>,
197{
198    future_value_internal(
199        rate,
200        periods as f64,
201        present_value.into(),
202        compounding.into().is_continuous(),
203    )
204}
205
206/// Calculates the value of an investment after it has grown or shrunk over time and returns a
207/// struct with the inputs and the calculated value. This is used for keeping track of a collection
208/// of financial scenarios so that they can be examined later.
209///
210/// See the [future_value](./index.html) module page for the formulas.
211///
212/// Related functions:
213/// * For simply calculating a single future value using a fixed rate use [`future_value`].
214/// * To calculate the future value if the rates vary by period use [`future_value_schedule`].
215/// * To calculate the future value with varying rates and return a struct that can produce the
216/// period-by-period values use [`future_value_schedule_solution`].
217///
218/// # Arguments
219/// * `rate` - The rate at which the investment grows or shrinks per period, expressed as a
220/// floating point number. For instance 0.05 would mean 5% growth. Often appears as `r` or `i` in
221/// formulas.
222/// * `periods` - The number of periods such as quarters or periods. Often appears as `n` or `t`.
223/// * `present_value` - The starting value of the investment. May appear as `pv` in formulas, or `C`
224/// for cash flow or `P` for principal.
225/// * `continuous_compounding` - True for continuous compounding, false for simple compounding.
226///
227/// # Errors
228/// Same domain rules as [`future_value`]: invalid rate or non-finite inputs yield
229/// [`crate::FinanceError`].
230///
231/// # Examples
232/// Calculate a future value and examine the period-by-period values.
233/// ```
234/// use finance_solution::*;
235/// // The rate is 1.2% per month.
236/// let rate = 0.012;
237///
238/// // The investment will grow for 8 months.
239/// let periods = 8;
240///
241/// // The initial investment is $200,000.
242/// let present_value = -200_000.0;
243///
244/// let continuous_compounding = false;
245///
246/// let solution = future_value_solution(rate, periods, present_value, continuous_compounding).unwrap();
247/// dbg!(&solution);
248///
249/// let future_value = solution.future_value();
250/// assert_rounded_4(future_value, 220_026.0467);
251///
252/// // Examine the formulas.
253/// let formula = solution.formula();
254/// dbg!(&formula);
255/// assert_eq!(formula, "220026.0467 = 200000.0000 * (1.012000 ^ 8)");
256/// let symbolic_formula = solution.symbolic_formula();
257/// dbg!(&symbolic_formula);
258/// assert_eq!(symbolic_formula, "fv = -pv * (1 + r)^n");
259///
260/// // Calculate the value at the end of each period.
261/// let series = solution.series();
262/// dbg!(&series);
263/// ```
264/// Create a collection of future value calculations ranging over several interest rates.
265/// ```
266/// # use finance_solution::*;
267///
268/// // The initial investment is $100,000.
269/// let present_value = -100_000.0;
270///
271/// // The investment will grow for 12 periods.
272/// let periods = 12;
273///
274/// let continuous_compounding = false;
275///
276/// // We'll keep a collection of the calculated future values along with their inputs.
277/// let mut scenarios = vec![];
278///
279/// for i in 2..=15 {
280///     // The rate is between 2% and 15% per year.
281///     let rate = f64::from(i) / 100.0;
282///     // Calculate the future value for this periodic rate and add the details to the collection.
283///     scenarios.push(future_value_solution(rate, periods, present_value, continuous_compounding).unwrap());
284/// }
285/// dbg!(&scenarios);
286/// assert_eq!(14, scenarios.len());
287///
288/// // Keep only the scenarios where the future value was between $200,000 and $400,000.
289/// scenarios.retain(|x| x.future_value() >= 200_000.00 && x.future_value() <= 400_000.00);
290/// dbg!(&scenarios);
291/// assert_eq!(7, scenarios.len());
292///
293/// // Check the formulas for the first of the remainingc scenarios.
294/// let formula = scenarios[0].formula();
295/// dbg!(&formula);
296/// assert_eq!("201219.6472 = 100000.0000 * (1.060000 ^ 12)", formula);
297/// let symbolic_formula = scenarios[0].symbolic_formula();
298/// dbg!(&symbolic_formula);
299/// assert_eq!("fv = -pv * (1 + r)^n", symbolic_formula);
300/// ```
301pub fn future_value_solution<T, C>(
302    rate: f64,
303    periods: u32,
304    present_value: T,
305    compounding: C,
306) -> crate::FinanceResult<TvmSolution>
307where
308    T: Into<f64> + Copy,
309    C: Into<crate::Compounding>,
310{
311    future_value_solution_internal(
312        rate,
313        periods as f64,
314        present_value.into(),
315        compounding.into().is_continuous(),
316    )
317}
318
319/// Calculates a future value based on rates that change for each period.
320///
321/// Related functions:
322/// * To calculate the future value with varying rates and return a struct that can produce the
323/// period-by-period values use [`future_value_schedule_solution`].
324/// * If there is a single fixed rate use [`future_value`] or [`future_value_solution`].
325///
326/// # Arguments
327/// * `rates` - A collection of rates, one for each period.
328/// * `present_value` - The starting value of the investment.
329///
330/// # Errors
331/// The call returns [`FinanceError`] if any of the rates is less than -1.0 as this would mean the investment is
332/// losing more than its full value.
333///
334/// # Examples
335/// Calculate the value of an investment whose rates vary by year.
336/// ```
337/// use finance_solution::*;
338/// // The rates vary by year: 4% followed by -3.9%, 10.6%, and -5.7%.
339/// let rates = [0.04, -0.039, 0.106, -0.057];
340///
341/// // The initial investment is $75,000.
342/// let present_value = -75_000.00;
343///
344/// let future_value = future_value_schedule(&rates, present_value).unwrap();
345/// dbg!(&future_value);
346/// assert_rounded_4(78_178.0458, future_value);
347/// ```
348/// Error case: One of the rates shows a drop of over 100%.
349/// ```
350/// # use finance_solution::{future_value_schedule, FinanceError};
351/// let rates = [0.116, -100.134, -0.09, 0.086];
352/// let present_value = -4_000.00;
353/// let err = future_value_schedule(&rates, present_value).unwrap_err();
354/// assert!(matches!(err, FinanceError::InvalidRate { .. }));
355/// ```
356///
357/// # Errors
358/// Returns [`FinanceError::InvalidRate`] if any rate is out of domain, or
359/// [`FinanceError::NonFinite`] for non-finite inputs/results.
360/// An empty `rates` slice is valid and means zero periods (returns `-present_value`).
361pub fn future_value_schedule<T>(rates: &[f64], present_value: T) -> crate::FinanceResult<f64>
362where
363    T: Into<f64> + Copy,
364{
365    let present_value = present_value.into();
366    crate::util::error::require_rates(rates)?;
367    crate::util::error::require_money("present_value", present_value)?;
368    let periods = rates.len();
369
370    let mut future_value = -present_value;
371    for i in 0..periods {
372        future_value *= 1.0 + rates[i];
373    }
374
375    if future_value.is_finite() {
376        Ok(future_value)
377    } else {
378        Err(crate::FinanceError::NonFinite {
379            field: "future_value",
380            value: future_value,
381        })
382    }
383}
384
385/// Calculates a future value based on rates that change for each period, returning a struct with
386/// all of the inputs and results.
387///
388/// Related functions:
389/// * For simply calculating a single future value using a fixed rate use [`future_value`].
390/// * To calculate a future value with a fixed rate and return a struct that shows the formula and
391/// optionally produces the the period-by-period values use [`future_value_solution`].
392/// * To calculate the future value if the rates vary by period use [`future_value_schedule`].
393///
394/// # Arguments
395/// * `rates` - A collection of rates, one for each period.
396/// * `present_value` - The starting value of the investment.
397///
398/// # Errors
399/// The call returns [`FinanceError`] if any of the rates is less than -1.0 as this would mean the investment is
400/// losing more than its full value.
401///
402/// # Examples
403/// Calculate the value of an investment whose rates vary by year.
404/// ```
405/// use finance_solution::*;
406/// // The rates vary by year: 8.1% followed by 11%, 4%, and -2.3%.
407/// let rates = [0.081, 0.11, 0.04, -0.023];
408///
409/// // The initial investment is $10,000.
410/// let present_value = -10_000.00;
411///
412/// let solution = future_value_schedule_solution(&rates, present_value).unwrap();
413/// dbg!(&solution);
414///
415/// let future_value = solution.future_value();
416/// dbg!(&future_value);
417/// assert_rounded_4(future_value, 12_192.0455);
418///
419/// // Calculate the value for each period.
420/// let series = solution.series();
421/// dbg!(&series);
422/// ```
423pub fn future_value_schedule_solution<T>(
424    rates: &[f64],
425    present_value: T,
426) -> crate::FinanceResult<TvmScheduleSolution>
427where
428    T: Into<f64> + Copy,
429{
430    let present_value = present_value.into();
431    let future_value = future_value_schedule(rates, present_value)?;
432    Ok(TvmScheduleSolution::new(
433        TvmVariable::FutureValue,
434        rates,
435        present_value,
436        future_value,
437    ))
438}
439
440pub(crate) fn future_value_internal(
441    rate: f64,
442    periods: f64,
443    present_value: f64,
444    continuous_compounding: bool,
445) -> crate::FinanceResult<f64> {
446    crate::util::error::require_rate(rate)?;
447    crate::util::error::require_money("present_value", present_value)?;
448    if rate.abs() > 1.0 {
449        warn!(
450            "You provided a periodic rate ({}) greater than 1. Are you sure you expect a {}% return?",
451            rate,
452            rate * 100.0
453        );
454    }
455    let future_value = if continuous_compounding {
456        // http://www.edmichaelreggie.com/TMVContent/rate.htm
457        -present_value * std::f64::consts::E.powf(rate * periods)
458    } else {
459        -present_value * (1.0 + rate).powf(periods)
460    };
461    if future_value.is_finite() {
462        Ok(future_value)
463    } else {
464        Err(crate::FinanceError::NonFinite {
465            field: "future_value",
466            value: future_value,
467        })
468    }
469}
470
471pub(crate) fn future_value_solution_internal(
472    rate: f64,
473    periods: f64,
474    present_value: f64,
475    continuous_compounding: bool,
476) -> crate::FinanceResult<TvmSolution> {
477    let future_value = future_value_internal(rate, periods, present_value, continuous_compounding)?;
478    let (formula, symbolic_formula) = if continuous_compounding {
479        let formula = format!(
480            "{:.4} = {:.4} * {:.6}^({:.6} * {})",
481            future_value,
482            -present_value,
483            std::f64::consts::E,
484            rate,
485            periods
486        );
487        let symbolic_formula = "fv = -pv * e^(rt)";
488        (formula, symbolic_formula)
489    } else {
490        let rate_multiplier = 1.0 + rate;
491        let formula = format!(
492            "{:.4} = {:.4} * ({:.6} ^ {})",
493            future_value, -present_value, rate_multiplier, periods
494        );
495        let symbolic_formula = "fv = -pv * (1 + r)^n";
496        (formula, symbolic_formula)
497    };
498    Ok(TvmSolution::new_fractional_periods(
499        TvmVariable::FutureValue,
500        continuous_compounding,
501        rate,
502        periods,
503        present_value,
504        future_value,
505        &formula,
506        symbolic_formula,
507    ))
508}
509
510#[cfg(test)]
511mod tests {
512    use super::*;
513    use crate::initialized_vector;
514
515    #[test]
516    fn test_future_value_error_rate_low() {
517        assert!(future_value(-101.0, 5, 250_000.00, false).is_err());
518    }
519
520    #[test]
521    fn test_future_value_solution_6() {
522        let rate_of_return = 1.0f64 / 0.0f64;
523        let periods = 6;
524        let present_value = 5_000.00;
525        assert!(future_value_solution(rate_of_return, periods, present_value, false).is_err());
526    }
527
528    #[test]
529    fn test_future_value_solution_7() {
530        let rate_of_return = 0.03;
531        let periods = 6;
532        let present_value = 1.0f64 / 0.0f64;
533        assert!(future_value_solution(rate_of_return, periods, present_value, false).is_err());
534    }
535
536    /*
537    macro_rules! compare_to_excel {
538        ( $r:expr, $n:expr, $pv:expr, $fv_excel:expr, $fv_manual_simple:expr, $fv_manual_cont:expr ) => {
539            println!("$r = {}, $n = {}, $pv = {}, $fv_excel: {}, $fv_manual_simple = {}, $fv_manual_cont = {}", $r, $n, $pv, $fv_excel, $fv_manual_simple, $fv_manual_cont);
540            assert_approx_equal!($fv_excel, $fv_manual_simple);
541
542            let fv_calc_simple = future_value($r, $n, $pv, false);
543            println!("fv_calc_simple = {}", fv_calc_simple);
544            assert_approx_equal!($fv_excel, fv_calc_simple);
545
546            let fv_calc_cont = future_value($r, $n, $pv, true);
547            println!("fv_calc_cont = {}", fv_calc_cont);
548            assert_approx_equal!($fv_manual_cont, fv_calc_cont);
549
550            let ratio = fv_calc_cont / fv_calc_simple;
551            println!("ratio = {}", ratio);
552            assert!(ratio >= 1.0);
553            assert!(ratio < 2.0);
554        }
555    }
556    */
557
558    fn compare_to_excel(
559        test_case: usize,
560        r: f64,
561        n: u32,
562        pv: f64,
563        fv_excel: f64,
564        fv_manual_simple: f64,
565        fv_manual_cont: f64,
566    ) {
567        let display = false;
568
569        if display {
570            println!("test_case = {}, r = {}, n = {}, pv = {}, fv_excel: {}, fv_manual_simple = {}, fv_manual_cont = {}", test_case, r, n, pv, fv_excel, fv_manual_simple, fv_manual_cont);
571        }
572        assert_approx_equal!(fv_excel, fv_manual_simple);
573
574        let fv_calc_simple = future_value(r, n, pv, false).unwrap();
575        if display {
576            println!("fv_calc_simple = {}", fv_calc_simple)
577        };
578        assert_approx_equal!(fv_excel, fv_calc_simple);
579
580        let fv_calc_cont = future_value(r, n, pv, true).unwrap();
581        if display {
582            println!("fv_calc_cont = {}", fv_calc_cont)
583        };
584        assert_approx_equal!(fv_manual_cont, fv_calc_cont);
585
586        let ratio = fv_calc_cont / fv_calc_simple;
587        if display {
588            println!("ratio = {}", ratio)
589        };
590        assert!(ratio >= 1.0);
591        assert!(ratio < 2.0);
592
593        // Solution with simple compounding.
594        let solution = future_value_solution(r, n, pv, false).unwrap();
595        if display {
596            dbg!(&solution);
597        }
598        solution.invariant();
599        assert!(solution.calculated_field().is_future_value());
600        assert_eq!(false, solution.continuous_compounding());
601        assert_approx_equal!(r, solution.rate());
602        assert_eq!(n, solution.periods());
603        assert_approx_equal!(n as f64, solution.fractional_periods());
604        assert_approx_equal!(pv, solution.present_value());
605        assert_approx_equal!(fv_excel, solution.future_value());
606
607        // Solution with continuous compounding.
608        let solution = future_value_solution(r, n, pv, true).unwrap();
609        if display {
610            dbg!(&solution);
611        }
612        solution.invariant();
613        assert!(solution.calculated_field().is_future_value());
614        assert!(solution.continuous_compounding());
615        assert_approx_equal!(r, solution.rate());
616        assert_eq!(n, solution.periods());
617        assert_approx_equal!(n as f64, solution.fractional_periods());
618        assert_approx_equal!(pv, solution.present_value());
619        assert_approx_equal!(fv_manual_cont, solution.future_value());
620
621        let rates = initialized_vector(n as usize, r);
622
623        // Schedule solution.
624        let solution = future_value_schedule_solution(&rates, pv).unwrap();
625        if display {
626            dbg!(&solution);
627        }
628        solution.invariant();
629        assert!(solution.calculated_field().is_future_value());
630        assert_eq!(n, solution.periods());
631        assert_approx_equal!(pv, solution.present_value());
632        assert_approx_equal!(fv_excel, solution.future_value());
633    }
634
635    #[test]
636    fn test_future_value_against_excel() {
637        compare_to_excel(
638            1,
639            0.01f64,
640            90,
641            1f64,
642            -2.44863267464848f64,
643            -2.44863267464848f64,
644            -2.45960311115695f64,
645        );
646        compare_to_excel(
647            2,
648            -0.01f64,
649            85,
650            -1.5f64,
651            0.638385185082981f64,
652            0.638385185082981f64,
653            0.64112239792309f64,
654        );
655        compare_to_excel(3, 0f64, 80, 2.25f64, -2.25f64, -2.25f64, -2.25f64);
656        compare_to_excel(
657            4,
658            0.05f64,
659            75,
660            -3.375f64,
661            131.060314992675f64,
662            131.060314992675f64,
663            143.508651750212f64,
664        );
665        compare_to_excel(
666            5,
667            -0.05f64,
668            70,
669            5.0625f64,
670            -0.139642432836174f64,
671            -0.139642432836174f64,
672            -0.152874253575487f64,
673        );
674        compare_to_excel(
675            6,
676            0.01f64,
677            65,
678            -7.59375f64,
679            14.499251769574f64,
680            14.499251769574f64,
681            14.5461381703243f64,
682        );
683        compare_to_excel(
684            7,
685            -0.01f64,
686            60,
687            11.390625f64,
688            -6.23245612973226f64,
689            -6.23245612973226f64,
690            -6.25130754238352f64,
691        );
692        compare_to_excel(
693            8,
694            0f64,
695            55,
696            -17.0859375f64,
697            17.0859375f64,
698            17.0859375f64,
699            17.0859375f64,
700        );
701        compare_to_excel(
702            9,
703            0.05f64,
704            50,
705            25.62890625f64,
706            -293.896914040351f64,
707            -293.896914040351f64,
708            -312.223995610061f64,
709        );
710        compare_to_excel(
711            10,
712            -0.05f64,
713            45,
714            -38.443359375f64,
715            3.82281753569715f64,
716            3.82281753569715f64,
717            4.05190026767808f64,
718        );
719        compare_to_excel(
720            11,
721            0.01f64,
722            40,
723            57.6650390625f64,
724            -85.8553853561044f64,
725            -85.8553853561044f64,
726            -86.0261294638861f64,
727        );
728        compare_to_excel(
729            12,
730            -0.01f64,
731            35,
732            -86.49755859375f64,
733            60.8465082158636f64,
734            60.8465082158636f64,
735            60.9537993307622f64,
736        );
737        compare_to_excel(
738            13,
739            0f64,
740            30,
741            129.746337890625f64,
742            -129.746337890625f64,
743            -129.746337890625f64,
744            -129.746337890625f64,
745        );
746        compare_to_excel(
747            14,
748            0.05f64,
749            25,
750            -194.619506835937f64,
751            659.050728569279f64,
752            659.050728569279f64,
753            679.288825069511f64,
754        );
755        compare_to_excel(
756            15,
757            -0.05f64,
758            20,
759            291.929260253906f64,
760            -104.652530140165f64,
761            -104.652530140165f64,
762            -107.3947731238f64,
763        );
764        compare_to_excel(
765            16,
766            0.01f64,
767            15,
768            -437.893890380859f64,
769            508.381212478371f64,
770            508.381212478371f64,
771            508.760116525988f64,
772        );
773        compare_to_excel(
774            17,
775            -0.01f64,
776            12,
777            656.840835571289f64,
778            -582.213779775772f64,
779            -582.213779775772f64,
780            -582.56556073855f64,
781        );
782        compare_to_excel(
783            18,
784            0f64,
785            10,
786            -985.261253356933f64,
787            985.261253356933f64,
788            985.261253356933f64,
789            985.261253356933f64,
790        );
791        compare_to_excel(
792            19,
793            0.05f64,
794            7,
795            1477.8918800354f64,
796            -2079.54228903805f64,
797            -2079.54228903805f64,
798            -2097.22840728772f64,
799        );
800        compare_to_excel(
801            20,
802            -0.05f64,
803            5,
804            -2216.8378200531f64,
805            1715.34684668614f64,
806            1715.34684668614f64,
807            1726.47503019966f64,
808        );
809        compare_to_excel(
810            21,
811            0.01f64,
812            4,
813            3325.25673007965f64,
814            -3460.27548760037f64,
815            -3460.27548760037f64,
816            -3460.96303162265f64,
817        );
818        compare_to_excel(
819            22,
820            -0.01f64,
821            3,
822            -4987.88509511947f64,
823            4839.73991990933f64,
824            4839.73991990933f64,
825            4840.47081241187f64,
826        );
827        compare_to_excel(
828            23,
829            0f64,
830            2,
831            7481.82764267921f64,
832            -7481.82764267921f64,
833            -7481.82764267921f64,
834            -7481.82764267921f64,
835        );
836        compare_to_excel(
837            24,
838            0.05f64,
839            1,
840            -11222.7414640188f64,
841            11783.8785372198f64,
842            11783.8785372198f64,
843            11798.1437232237f64,
844        );
845        compare_to_excel(
846            25,
847            -0.05f64,
848            0,
849            16834.1121960282f64,
850            -16834.1121960282f64,
851            -16834.1121960282f64,
852            -16834.1121960282f64,
853        );
854    }
855}