r23494 - gnucash/trunk/src - Test for overflow limits in gnc_numeric_add.

John Ralls jralls at code.gnucash.org
Thu Dec 5 16:54:28 EST 2013


Author: jralls
Date: 2013-12-05 16:54:28 -0500 (Thu, 05 Dec 2013)
New Revision: 23494
Trac: http://svn.gnucash.org/trac/changeset/23494

Added:
   gnucash/trunk/src/libqof/qof/test/test-gnc-numeric.c
Modified:
   gnucash/trunk/src/engine/test/test-numeric.c
   gnucash/trunk/src/libqof/qof/gnc-numeric.c
Log:
Test for overflow limits in gnc_numeric_add.

For analysis of Bug 665707.

Modified: gnucash/trunk/src/engine/test/test-numeric.c
===================================================================
--- gnucash/trunk/src/engine/test/test-numeric.c	2013-12-05 21:54:18 UTC (rev 23493)
+++ gnucash/trunk/src/engine/test/test-numeric.c	2013-12-05 21:54:28 UTC (rev 23494)
@@ -546,8 +546,113 @@
     }
 }
 
+extern gint64 pwr64 (gint64 op, int exp);
+
+static void
+check_add_subtract_overflow (void)
+{
+    int i;
+
+    for (i = 0; i < NREPS; i++)
+    {
+	/* Div to avoid addition overflows; we're looking for lcd conversion overflows here. */
+
+	int exp_a = rand () % 1000;
+	int exp_b = rand () % 1000;
+        gint64 bin_deno_a = (exp_a == 0 ? 1 : exp_a);
+	gint64 bin_deno_b = (exp_b == 0 ? 1 : exp_b);
+/*
+	int exp_a = rand () % 11;
+	int exp_b = rand () % 11;
+	gint64 bin_deno_a = (1 << exp_a);
+	gint64 bin_deno_b = (1 << exp_a);
+*/
+	gint64 dec_deno_a = pwr64 (10, exp_a % 7);
+	gint64 dec_deno_b = pwr64 (10, exp_b % 7);
+        gint64 na = get_random_gint64 () % (1000000 * dec_deno_a);
+        gint64 nb = get_random_gint64 () % (1000000 * dec_deno_b);
+	gnc_numeric result;
+	GNCNumericErrorCode err;
+	gchar *errmsg;
+
+        gnc_numeric ba = gnc_numeric_create(na, bin_deno_a);
+        gnc_numeric bb = gnc_numeric_create(nb, bin_deno_b);
+        gnc_numeric da = gnc_numeric_create(na, dec_deno_a);
+        gnc_numeric db = gnc_numeric_create(nb, dec_deno_b);
+	gchar *ba_str = gnc_numeric_to_string (ba);
+	gchar *bb_str = gnc_numeric_to_string (bb);
+	gchar *da_str = gnc_numeric_to_string (da);
+	gchar *db_str = gnc_numeric_to_string (db);
+
+
+        /* Add */
+
+	result = gnc_numeric_add(ba, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
+	err = gnc_numeric_check (result);
+	errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, bb_str,
+				  gnc_numeric_errorCode_to_string (err));
+	do_test (err == 0, errmsg);
+	g_free (errmsg);
+
+	result = gnc_numeric_add(da, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
+	err = gnc_numeric_check (result);
+	errmsg = g_strdup_printf ("%s + %s raised %s", da_str, bb_str,
+				  gnc_numeric_errorCode_to_string (err));
+	do_test (err == 0, errmsg);
+	g_free (errmsg);
+	result = gnc_numeric_add(ba, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
+	err = gnc_numeric_check (result);
+	errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, db_str,
+				  gnc_numeric_errorCode_to_string (err));
+	do_test (err == 0, errmsg);
+	g_free (errmsg);
+
+	result = gnc_numeric_add(da, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
+	err = gnc_numeric_check (result);
+	errmsg = g_strdup_printf ("%s + %s raised %s", da_str, db_str,
+				  gnc_numeric_errorCode_to_string (err));
+	do_test (err == 0, errmsg);
+	g_free (errmsg);
+        /* Subtract */
+
+	result = gnc_numeric_sub(ba, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
+	err = gnc_numeric_check (result);
+	errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, bb_str,
+				  gnc_numeric_errorCode_to_string (err));
+	do_test (err == 0, errmsg);
+	g_free (errmsg);
+
+	result = gnc_numeric_sub(da, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
+	err = gnc_numeric_check (result);
+	errmsg = g_strdup_printf ("%s + %s raised %s", da_str, bb_str,
+				  gnc_numeric_errorCode_to_string (err));
+	do_test (err == 0, errmsg);
+	g_free (errmsg);
+	result = gnc_numeric_sub(ba, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
+	err = gnc_numeric_check (result);
+	errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, db_str,
+				  gnc_numeric_errorCode_to_string (err));
+	do_test (err == 0, errmsg);
+	g_free (errmsg);
+
+	result = gnc_numeric_sub(da, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
+	err = gnc_numeric_check (result);
+	errmsg = g_strdup_printf ("%s + %s raised %s", da_str, db_str,
+				  gnc_numeric_errorCode_to_string (err));
+	do_test (err == 0, errmsg);
+	g_free (errmsg);
+
+	g_free (ba_str);
+	g_free (bb_str);
+	g_free (da_str);
+	g_free (db_str);
+    }
+
+}
+
 /* ======================================================= */
 
+
 static void
 check_mult_div (void)
 {
@@ -871,6 +976,7 @@
     check_double();
     check_neg();
     check_add_subtract();
+    check_add_subtract_overflow ();
     check_mult_div ();
     check_reciprocal();
 }

Modified: gnucash/trunk/src/libqof/qof/gnc-numeric.c
===================================================================
--- gnucash/trunk/src/libqof/qof/gnc-numeric.c	2013-12-05 21:54:18 UTC (rev 23493)
+++ gnucash/trunk/src/libqof/qof/gnc-numeric.c	2013-12-05 21:54:28 UTC (rev 23494)
@@ -45,6 +45,23 @@
 
 /* static short module = MOD_ENGINE; */
 
+gint64
+pwr64 (gint64 op, int exp)
+{
+    qofint128 tmp;
+    if (exp == 0) return 1;
+    if (exp % 2)
+    {
+	tmp = mult128 (op, pwr64 (op, exp - 1));
+	if (tmp.isbig) return 0;
+	return tmp.lo;
+    }
+    tmp.lo = pwr64 (op, exp / 2);
+    tmp = mult128 (tmp.lo, tmp.lo);
+    if (tmp.isbig) return 0;
+    return tmp.lo;
+}
+
 /* =============================================================== */
 /* This function is small, simple, and used everywhere below,
  * lets try to inline it.

Added: gnucash/trunk/src/libqof/qof/test/test-gnc-numeric.c
===================================================================
--- gnucash/trunk/src/libqof/qof/test/test-gnc-numeric.c	                        (rev 0)
+++ gnucash/trunk/src/libqof/qof/test/test-gnc-numeric.c	2013-12-05 21:54:28 UTC (rev 23494)
@@ -0,0 +1,47 @@
+/********************************************************************
+ * test_qofbackend.c: GLib g_test test suite for qofbackend.	    *
+ * Copyright 2011 John Ralls <jralls at ceridwen.us>		    *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+\********************************************************************/
+#include "config.h"
+#include <string.h>
+#include <glib.h>
+#include <qof.h>
+#include <unittest-support.h>
+
+static const gchar *suitename = "/qof/gnc_numeric";
+
+static void
+test_gnc_numeric_add (void)
+{
+    gnc_numeric a = { 123456789987654321, 1000000000 };
+    gnc_numeric b = { 65432198765432198, 100000000 };
+    gnc_numeric goal_ab = { 777778777641976301, 1000000000 };
+    gnc_numeric result;
+
+    result = gnc_numeric_add (a, b, GNC_DENOM_AUTO,
+			      GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
+    g_assert (gnc_numeric_equal (result, goal_ab));
+}
+
+void
+test_suite_gnc_numeric ( void )
+{
+    GNC_TEST_ADD_FUNC( suitename, "gnc-numeric add", test_gnc_numeric_add );
+}



More information about the gnucash-changes mailing list