[gnumeric] VDB: Fix interpretation of partial periods.



commit c4a954cfaadbe49c32a862696f8b33a6ed3ed6c7
Author: Morten Welinder <terra gnome org>
Date:   Tue Nov 14 19:36:38 2017 -0500

    VDB: Fix interpretation of partial periods.
    
    Inspired by AOO and LO bug reports.

 NEWS                           |    1 +
 plugins/fn-financial/ChangeLog |    5 +++
 plugins/fn-financial/sc-fin.c  |   58 ++++++++++++++++++++++++++--------------
 3 files changed, 44 insertions(+), 20 deletions(-)
---
diff --git a/NEWS b/NEWS
index 77a51c6..7c9fc6f 100644
--- a/NEWS
+++ b/NEWS
@@ -22,6 +22,7 @@ Morten:
        * Fix xlsx export of trend line equation.
        * Fix xlsx import and export of trend line affine flag.
        * Avoid critical on exporting unnamed xlsx trend line.
+       * Fix VDB for partial periods.  [#790299]
 
 --------------------------------------------------------------------------
 Gnumeric 1.12.35
diff --git a/plugins/fn-financial/ChangeLog b/plugins/fn-financial/ChangeLog
index 7ef1035..09709e0 100644
--- a/plugins/fn-financial/ChangeLog
+++ b/plugins/fn-financial/ChangeLog
@@ -1,3 +1,8 @@
+2017-11-13  Morten Welinder  <terra gnome org>
+
+       * sc-fin.c (get_vdb): Fix handling of partial periods.  Protect
+       against crazy values.
+
 2017-07-06  Morten Welinder <terra gnome org>
 
        * Release 1.12.35
diff --git a/plugins/fn-financial/sc-fin.c b/plugins/fn-financial/sc-fin.c
index affbe71..e81ed2d 100644
--- a/plugins/fn-financial/sc-fin.c
+++ b/plugins/fn-financial/sc-fin.c
@@ -386,13 +386,18 @@ get_vdb (gnm_float cost, gnm_float salvage, gnm_float life,
        gnm_float fVdb;
        gnm_float fIntStart = gnm_floor (start_period);
        gnm_float fIntEnd   = gnm_ceil (end_period);
-       int        i;
-       int        nLoopStart = (int) fIntStart;
-       int        nLoopEnd   = (int) fIntEnd;
 
        fVdb      = 0.0;
 
        if ( flag ) {
+               int i, nLoopStart, nLoopEnd;
+
+               if (fIntEnd > G_MAXINT ||
+                   fIntEnd - fIntStart > 10000 /* arbitrary */)
+                       return value_new_error_VALUE (NULL);
+
+               nLoopStart = (int) fIntStart;
+               nLoopEnd   = (int) fIntEnd;
                for (i = nLoopStart + 1; i <= nLoopEnd; i++) {
                        gnm_float fTerm;
 
@@ -405,23 +410,36 @@ get_vdb (gnm_float cost, gnm_float salvage, gnm_float life,
                        fVdb += fTerm;
                }
        } else {
-               gnm_float life1 = life;
-               gnm_float fPart;
-
-               if ( start_period != gnm_floor (start_period) )
-                       if (factor > 1) {
-                               if (start_period >= life / 2) {
-                                       fPart        = start_period - life / 2;
-                                       start_period = life / 2;
-                                       end_period  -= fPart;
-                                       life1       += 1;
-                               }
-                       }
-
-               cost -= ScInterVDB (cost, salvage, life, life1, start_period,
-                                   factor);
-               fVdb = ScInterVDB (cost, salvage, life, life - start_period,
-                                  end_period - start_period, factor);
+               gnm_float fPart = 0;
+               double fIntEnd = gnm_ceil (end_period);
+
+               if (start_period > fIntStart) {
+                        // First period is partial.  Calculate the excess as
+                       // the pro-rata value of the first period as-if it
+                       // was not partial.
+                        double tempcost = cost -
+                               ScInterVDB( cost, salvage, life, life, fIntStart, factor);
+                        fPart += (start_period - fIntStart) *
+                               ScInterVDB( tempcost, salvage, life, life - fIntStart,
+                                           1, factor);
+               }
+
+               if (end_period < fIntEnd) {
+                        // Last period is partial.  Calculate the excess as
+                       // the pro-rata value of the last period as-if it
+                       // was not partial.
+                        double em1 = fIntEnd - 1; // Start of last period
+                        double tempcost = cost -
+                            ScInterVDB (cost, salvage, life, life, em1, factor);
+                        fPart += (fIntEnd - end_period) *
+                            ScInterVDB (tempcost, salvage, life, life - em1,
+                                       1, factor);
+               }
+
+               cost -= ScInterVDB (cost, salvage, life, life, fIntStart, factor);
+               fVdb = ScInterVDB (cost, salvage, life, life - fIntStart,
+                                  fIntEnd - fIntStart, factor);
+               fVdb -= fPart;
        }
        return value_new_float (fVdb);
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]