static guint ght_gnc_numeric_hash(gconstpointer v1) { gnc_numeric n1 = *(gnc_numeric *)v1; gdouble d1 = gnc_numeric_to_double(n1); return g_str_hash(&d1); }
static void check_double (void) { double flo; gnc_numeric val = gnc_numeric_create (0, 1); check_unary_op (gnc_numeric_eq, gnc_numeric_create (112346, 100000), double_to_gnc_numeric(1.1234567890123, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), val, "expected %s = %s double 6 figs"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (112346, 10000000), double_to_gnc_numeric(0.011234567890123, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), val, "expected %s = %s double 6 figs"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (112346, 100), double_to_gnc_numeric(1123.4567890123, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), val, "expected %s = %s double 6 figs"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (112346, 10000000000LL), double_to_gnc_numeric(1.1234567890123e-5, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), val, "expected %s = %s double 6 figs"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (961600000, 10000000), double_to_gnc_numeric(96.16, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(9) | GNC_HOW_RND_ROUND), val, "expected %s = %s GncNumeric from 96.16"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (9616000000, 1), double_to_gnc_numeric(9616000000.0, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(9) | GNC_HOW_RND_ROUND), val, "expected %s = %s GncNumeric from 9616000000.0"); flo = gnc_numeric_to_double(gnc_numeric_create(7, 16)); do_test ((0.4375 == flo), "float pt conversion"); }
static void convert_query_term_to_sql( const GncSqlBackend* be, const gchar* fieldName, QofQueryTerm* pTerm, GString* sql ) { QofQueryPredData* pPredData; gboolean isInverted; g_return_if_fail( pTerm != NULL ); g_return_if_fail( sql != NULL ); pPredData = qof_query_term_get_pred_data( pTerm ); isInverted = qof_query_term_is_inverted( pTerm ); if ( g_strcmp0( pPredData->type_name, QOF_TYPE_GUID ) == 0 ) { query_guid_t guid_data = (query_guid_t)pPredData; GList* guid_entry; g_string_append( sql, "(" ); g_string_append( sql, fieldName ); switch ( guid_data->options ) { case QOF_GUID_MATCH_ANY: if ( isInverted ) g_string_append( sql, " NOT IN (" ); else g_string_append( sql, " IN (" ); break; case QOF_GUID_MATCH_NONE: if ( isInverted ) g_string_append( sql, " IN (" ); else g_string_append( sql, " NOT IN (" ); break; default: PERR( "Unexpected GncGUID match type: %d\n", guid_data->options ); } for ( guid_entry = guid_data->guids; guid_entry != NULL; guid_entry = guid_entry->next ) { gchar guid_buf[GUID_ENCODING_LENGTH+1]; if ( guid_entry != guid_data->guids ) g_string_append( sql, "," ); (void)guid_to_string_buff( guid_entry->data, guid_buf ); g_string_append_printf( sql, "'%s'", guid_buf ); } g_string_append( sql, "))" ); } else if ( g_strcmp0( pPredData->type_name, QOF_TYPE_CHAR ) == 0 ) { query_char_t char_data = (query_char_t)pPredData; int i; if ( isInverted ) { g_string_append( sql, "NOT(" ); } if ( char_data->options == QOF_CHAR_MATCH_NONE ) { g_string_append( sql, "NOT " ); } g_string_append( sql, "(" ); for ( i = 0; char_data->char_list[i] != '\0'; i++ ) { if ( i != 0 ) { g_string_append( sql, " OR " ); } g_string_append( sql, fieldName ); g_string_append( sql, " = '" ); g_string_append_c( sql, char_data->char_list[i] ); g_string_append( sql, "'" ); } g_string_append( sql, ") " ); if ( isInverted ) { g_string_append( sql, ") " ); } } else if ( g_strcmp0( pPredData->type_name, QOF_TYPE_STRING ) == 0 ) { query_string_t string_data = (query_string_t)pPredData; sqlEscape* escape = sqlEscape_new(); if ( isInverted ) { g_string_append( sql, "NOT(" ); } if ( pPredData->how == QOF_COMPARE_NEQ ) { g_string_append( sql, "NOT(" ); } g_string_append( sql, fieldName ); if ( string_data->is_regex || string_data->options == QOF_STRING_MATCH_CASEINSENSITIVE ) { PWARN( "String is_regex || option = QOF_STRING_MATCH_INSENSITIVE\n" ); } // g_string_append( sql, " ~" ); // } else { g_string_append( sql, " =" ); // } // if( string_data->options == QOF_STRING_MATCH_CASEINSENSITIVE ) { // g_string_append( sql, "*" ); // } g_string_append( sql, "'" ); g_string_append( sql, sqlEscapeString( escape, string_data->matchstring ) ); g_string_append( sql, "'" ); if ( pPredData->how == QOF_COMPARE_NEQ ) { g_string_append( sql, ")" ); } if ( isInverted ) { g_string_append( sql, ")" ); } sqlEscape_destroy( escape ); } else { g_string_append( sql, "(" ); g_string_append( sql, fieldName ); convert_query_comparison_to_sql( pPredData, isInverted, sql ); if ( strcmp( pPredData->type_name, QOF_TYPE_NUMERIC ) == 0 ) { query_numeric_t pData = (query_numeric_t)pPredData; double d = gnc_numeric_to_double( pData->amount ); g_string_append_printf( sql, "%f", d ); } else if ( g_strcmp0( pPredData->type_name, QOF_TYPE_DATE ) == 0 ) { query_date_t date_data = (query_date_t)pPredData; gchar* datebuf; datebuf = gnc_sql_convert_timespec_to_string( be, date_data->date ); g_string_append_printf( sql, "'%s'", datebuf ); } else if ( strcmp( pPredData->type_name, QOF_TYPE_INT32 ) == 0 ) { query_int32_t pData = (query_int32_t)pPredData; g_string_append_printf( sql, "%d", pData->val ); } else if ( strcmp( pPredData->type_name, QOF_TYPE_INT64 ) == 0 ) { query_int64_t pData = (query_int64_t)pPredData; g_string_append_printf( sql, "%" G_GINT64_FORMAT, pData->val ); } else if ( strcmp( pPredData->type_name, QOF_TYPE_DOUBLE ) == 0 ) { query_double_t pData = (query_double_t)pPredData; g_string_append_printf( sql, "%f", pData->val ); } else if ( strcmp( pPredData->type_name, QOF_TYPE_BOOLEAN ) == 0 ) { query_boolean_t pData = (query_boolean_t)pPredData; g_string_append_printf( sql, "%d", pData->val ); } else { PERR( "Unknown query predicate type: %s\n", pPredData->type_name ); } g_string_append( sql, ")" ); } }
gnc_numeric gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how) { gnc_numeric out; gnc_numeric temp; gint64 temp_bc; gint64 temp_a; gint64 remainder; gint64 sign; gint denom_neg = 0; double ratio, logratio; double sigfigs; qofint128 nume, newm; temp.num = 0; temp.denom = 0; if (gnc_numeric_check(in)) { return gnc_numeric_error(GNC_ERROR_ARG); } if (denom == GNC_DENOM_AUTO) { switch (how & GNC_NUMERIC_DENOM_MASK) { default: case GNC_HOW_DENOM_LCD: /* LCD is meaningless with AUTO in here */ case GNC_HOW_DENOM_EXACT: return in; break; case GNC_HOW_DENOM_REDUCE: /* reduce the input to a relatively-prime fraction */ return gnc_numeric_reduce(in); break; case GNC_HOW_DENOM_FIXED: if (in.denom != denom) { return gnc_numeric_error(GNC_ERROR_DENOM_DIFF); } else { return in; } break; case GNC_HOW_DENOM_SIGFIG: ratio = fabs(gnc_numeric_to_double(in)); if (ratio < 10e-20) { logratio = 0; } else { logratio = log10(ratio); logratio = ((logratio > 0.0) ? (floor(logratio) + 1.0) : (ceil(logratio))); } sigfigs = GNC_HOW_GET_SIGFIGS(how); if (fabs(sigfigs - logratio) > 18) return gnc_numeric_error(GNC_ERROR_OVERFLOW); if (sigfigs - logratio >= 0) { denom = (gint64)(pow(10, sigfigs - logratio)); } else { denom = -((gint64)(pow(10, logratio - sigfigs))); } how = how & ~GNC_HOW_DENOM_SIGFIG & ~GNC_NUMERIC_SIGFIGS_MASK; break; } } /* Make sure we need to do the work */ if (in.denom == denom) { return in; } if (in.num == 0) { out.num = 0; out.denom = denom; return out; } /* If the denominator of the input value is negative, get rid of that. */ if (in.denom < 0) { in.num = in.num * (- in.denom); /* BUG: overflow not handled. */ in.denom = 1; } sign = (in.num < 0) ? -1 : 1; /* If the denominator is less than zero, we are to interpret it as * the reciprocal of its magnitude. */ if (denom < 0) { /* XXX FIXME: use 128-bit math here ... */ denom = - denom; denom_neg = 1; temp_a = (in.num < 0) ? -in.num : in.num; temp_bc = in.denom * denom; /* BUG: overflow not handled. */ remainder = temp_a % temp_bc; out.num = temp_a / temp_bc; out.denom = - denom; } else { /* Do all the modulo and int division on positive values to make * things a little clearer. Reduce the fraction denom/in.denom to * help with range errors */ temp.num = denom; temp.denom = in.denom; temp = gnc_numeric_reduce(temp); /* Symbolically, do the following: * out.num = in.num * temp.num; * remainder = out.num % temp.denom; * out.num = out.num / temp.denom; * out.denom = denom; */ nume = mult128 (in.num, temp.num); newm = div128 (nume, temp.denom); remainder = rem128 (nume, temp.denom); if (newm.isbig) { return gnc_numeric_error(GNC_ERROR_OVERFLOW); } out.num = newm.lo; out.denom = denom; } if (remainder) { switch (how & GNC_NUMERIC_RND_MASK) { case GNC_HOW_RND_FLOOR: if (sign < 0) { out.num = out.num + 1; } break; case GNC_HOW_RND_CEIL: if (sign > 0) { out.num = out.num + 1; } break; case GNC_HOW_RND_TRUNC: break; case GNC_HOW_RND_PROMOTE: out.num = out.num + 1; break; case GNC_HOW_RND_ROUND_HALF_DOWN: if (denom_neg) { if ((2 * remainder) > in.denom * denom) { out.num = out.num + 1; } } else if ((2 * remainder) > temp.denom) { out.num = out.num + 1; } /* check that 2*remainder didn't over-flow */ else if (((2 * remainder) < remainder) && (remainder > (temp.denom / 2))) { out.num = out.num + 1; } break; case GNC_HOW_RND_ROUND_HALF_UP: if (denom_neg) { if ((2 * remainder) >= in.denom * denom) { out.num = out.num + 1; } } else if ((2 * remainder ) >= temp.denom) { out.num = out.num + 1; } /* check that 2*remainder didn't over-flow */ else if (((2 * remainder) < remainder) && (remainder >= (temp.denom / 2))) { out.num = out.num + 1; } break; case GNC_HOW_RND_ROUND: if (denom_neg) { if ((2 * remainder) > in.denom * denom) { out.num = out.num + 1; } else if ((2 * remainder) == in.denom * denom) { if (out.num % 2) { out.num = out.num + 1; } } } else { if ((2 * remainder ) > temp.denom) { out.num = out.num + 1; } /* check that 2*remainder didn't over-flow */ else if (((2 * remainder) < remainder) && (remainder > (temp.denom / 2))) { out.num = out.num + 1; } else if ((2 * remainder) == temp.denom) { if (out.num % 2) { out.num = out.num + 1; } } /* check that 2*remainder didn't over-flow */ else if (((2 * remainder) < remainder) && (remainder == (temp.denom / 2))) { if (out.num % 2) { out.num = out.num + 1; } } } break; case GNC_HOW_RND_NEVER: return gnc_numeric_error(GNC_ERROR_REMAINDER); break; } } out.num = (sign > 0) ? out.num : (-out.num); return out; }
static void _var_numeric_to_string(gnc_numeric *value, GString **str) { *str = g_string_sized_new(5); g_string_printf(*str, "%0.2f", gnc_numeric_to_double(*value)); }
static void check_reciprocal(void) { gnc_numeric a, b, ans, val; double flo; val = gnc_numeric_create(-60, 20); check_unary_op (gnc_numeric_eq, gnc_numeric_create (-3, -1), gnc_numeric_convert(val, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER), val, "expected %s got %s = (%s as RECIP(1))"); a = gnc_numeric_create(200, 100); b = gnc_numeric_create(300, 100); /* 2 + 3 = 5 */ ans = gnc_numeric_add(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER); check_binary_op (gnc_numeric_create(5, -1), ans, a, b, "expected %s got %s = %s + %s for reciprocal"); /* 2 + 3 = 5 */ a = gnc_numeric_create(2, -1); b = gnc_numeric_create(300, 100); ans = gnc_numeric_add(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER); check_binary_op (gnc_numeric_create(5, -1), ans, a, b, "expected %s got %s = %s + %s for reciprocal"); /* check gnc_numeric_to_double */ flo = gnc_numeric_to_double(gnc_numeric_create(5, -1)); do_test ((5.0 == flo), "reciprocal conversion"); /* check gnc_numeric_compare */ a = gnc_numeric_create(2, 1); b = gnc_numeric_create(2, -1); do_test((0 == gnc_numeric_compare(a, b)), " 2 == 2 "); a = gnc_numeric_create(2, 1); b = gnc_numeric_create(3, -1); do_test((-1 == gnc_numeric_compare(a, b)), " 2 < 3 "); a = gnc_numeric_create(-2, 1); b = gnc_numeric_create(2, -1); do_test((-1 == gnc_numeric_compare(a, b)), " -2 < 2 "); a = gnc_numeric_create(2, -1); b = gnc_numeric_create(3, -1); do_test((-1 == gnc_numeric_compare(a, b)), " 2 < 3 "); /* check for equality */ a = gnc_numeric_create(2, 1); b = gnc_numeric_create(2, -1); do_test(gnc_numeric_equal(a, b), " 2 == 2 "); /* check gnc_numeric_mul */ a = gnc_numeric_create(2, 1); b = gnc_numeric_create(3, -1); ans = gnc_numeric_mul(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER); check_binary_op (gnc_numeric_create(6, -1), ans, a, b, "expected %s got %s = %s * %s for reciprocal"); /* check gnc_numeric_div */ /* -60 / 20 = -3 */ a = gnc_numeric_create(-60, 1); b = gnc_numeric_create(2, -10); ans = gnc_numeric_div(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER); check_binary_op (gnc_numeric_create(-3, -1), ans, a, b, "expected %s got %s = %s / %s for reciprocal"); /* 60 / 20 = 3 */ a = gnc_numeric_create(60, 1); b = gnc_numeric_create(2, -10); ans = gnc_numeric_div(a, b, GNC_DENOM_RECIPROCAL(1), GNC_HOW_RND_NEVER); check_binary_op (gnc_numeric_create(3, -1), ans, a, b, "expected %s got %s = %s / %s for reciprocal"); }
static void* func_op(const char *fname, int argc, void **argv) { SCM scmFn, scmArgs, scmTmp; int i; var_store *vs; gchar *str; gnc_numeric n, *result; GString *realFnName; realFnName = g_string_sized_new( strlen(fname) + 5 ); g_string_printf( realFnName, "gnc:%s", fname ); scmFn = scm_internal_catch(SCM_BOOL_T, (scm_t_catch_body)scm_c_eval_string, realFnName->str, scm_handle_by_message_noexit, NULL); g_string_free( realFnName, TRUE ); if (!scm_is_procedure(scmFn)) { /* FIXME: handle errors correctly. */ printf( "gnc:\"%s\" is not a scm procedure\n", fname ); return NULL; } scmArgs = scm_listify( SCM_UNDEFINED ); for ( i = 0; i < argc; i++ ) { /* cons together back-to-front. */ vs = (var_store*)argv[argc - i - 1]; switch ( vs->type ) { case VST_NUMERIC: n = *(gnc_numeric*)(vs->value); scmTmp = scm_make_real( gnc_numeric_to_double( n ) ); break; case VST_STRING: str = (char*)(vs->value); scmTmp = scm_mem2string( str, strlen(str) ); break; default: /* FIXME: error */ printf( "argument %d not a numeric or string [type = %d]\n", i, vs->type ); return NULL; break; /* notreached */ } scmArgs = scm_cons( scmTmp, scmArgs ); } //scmTmp = scm_apply(scmFn, scmArgs , SCM_EOL); scmTmp = gfec_apply(scmFn, scmArgs, _exception_handler); if (_function_evaluation_error_msg != NULL) { PERR("function eval error: [%s]\n", _function_evaluation_error_msg); _function_evaluation_error_msg = NULL; return NULL; } result = g_new0( gnc_numeric, 1 ); *result = double_to_gnc_numeric( scm_num2dbl(scmTmp, G_STRFUNC), GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND ); /* FIXME: cleanup scmArgs = scm_list, cons'ed cells? */ return (void*)result; }