mirror of https://github.com/Gnucash/gnucash
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
388 lines
11 KiB
388 lines
11 KiB
/***************************************************************************
|
|
numeric_ops.c - description
|
|
-------------------
|
|
begin : Wednesday June 21 2000
|
|
email : tboldt@attglobal.net
|
|
Author : Terry D. Boldt
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* 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. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
/*
|
|
* Functions to execute arthmetic operators on integer and double operands
|
|
* 6-23-2000
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <float.h>
|
|
#include <math.h>
|
|
|
|
#define NUMERIC_OPS_STATICS
|
|
#include "finvar.h"
|
|
|
|
static double neg_table[] = {
|
|
1e-256,
|
|
1e-128,
|
|
1e-64,
|
|
1e-32,
|
|
1e-16,
|
|
1e-8,
|
|
1e-4,
|
|
1e-2,
|
|
1e-1,
|
|
1.0
|
|
};
|
|
|
|
static double pos_table[] = {
|
|
1e+256,
|
|
1e+128,
|
|
1e+64,
|
|
1e+32,
|
|
1e+16,
|
|
1e+8,
|
|
1e+4,
|
|
1e+2,
|
|
1e+1
|
|
};
|
|
|
|
#define MAX_SCALE ((LONG_MAX - 10) / 10)
|
|
|
|
/* function to translate ASCII string to numeric format.
|
|
*
|
|
* Recognizes either integer numerics or floating point numerics.
|
|
* Recognizes integers in format:
|
|
* (sign)digit_sequence
|
|
* digit_sequence may contain a grouping character, the grouping character is ignored
|
|
* optional sign == '+' or '-'
|
|
*
|
|
* Recognizes floating point in formats:
|
|
* (sign)digit_sequence.digits(exp)
|
|
* (sign)digit_sequence.(exp)
|
|
* (sign)digit_sequence(exp)
|
|
* (sign).digits(exp)
|
|
* '.' represents the radix point passed, digit_sequence may contain a grouping character
|
|
* the grouping character is ignored
|
|
* optional sign == '+' or '-'
|
|
* optional exp == ('e' or 'E')(sign)digits
|
|
*
|
|
* Terminates on first unrecognized character.
|
|
*
|
|
*/
|
|
void *trans_numeric(
|
|
const char *str, /* pointer to string to translate */
|
|
char radix_point, /* radix character */
|
|
char group_char, /* grouping character to left of radix */
|
|
char **endstr) /* where to return pointer to first unrecognized character */
|
|
{
|
|
double dblval = 0.0;
|
|
int exp = 0,
|
|
dchr,
|
|
err = 0,
|
|
base = 10;
|
|
long int inum = 0;
|
|
unsigned long msdec = 0,
|
|
lsdec = 0,
|
|
msscale = 1;
|
|
unsigned radix = 0,
|
|
sign = 0,
|
|
digit_cnt = 0;
|
|
const char *strinit = str;
|
|
numeric_ptr rslt = NULL;
|
|
|
|
while ( isspace(*str) ) str++;
|
|
|
|
switch (*str) {
|
|
case '-':
|
|
sign++;
|
|
case '+':
|
|
str++;
|
|
default:
|
|
break;
|
|
} /* endswitch */
|
|
|
|
while ( *str ) {
|
|
|
|
while ( (*str >= '0') && (*str <= '9') ) {
|
|
digit_cnt++;
|
|
|
|
if ( msdec < MAX_SCALE ) msdec = msdec * 10 + (*str - '0');
|
|
else if ( msscale < MAX_SCALE ) {
|
|
lsdec = lsdec * 10 + (*str - '0');
|
|
msscale *= 10;
|
|
} else exp++;
|
|
|
|
if ( radix ) exp--;
|
|
else {
|
|
dchr = *str - '0';
|
|
if ( ((LONG_MIN + dchr) / base) > inum ) err = 1;
|
|
inum = inum * base + dchr;
|
|
} /* endif */
|
|
str++;
|
|
} /* endwhile */
|
|
|
|
if ( !radix ) {
|
|
if ( *str == radix_point ) radix++;
|
|
else if ( *str != group_char ) break;
|
|
} else {
|
|
break;
|
|
} /* endif */
|
|
|
|
str++;
|
|
} /* endwhile */
|
|
|
|
if ( digit_cnt ) {
|
|
unsigned exp_dcnt = 0;
|
|
|
|
if ( (*str == 'e') || (*str == 'E') ) {
|
|
char exp_sign = EOS;
|
|
int ex_exp = 0;
|
|
|
|
switch (*++str) {
|
|
case '-':
|
|
exp_sign++;
|
|
case '+':
|
|
str++;
|
|
default:
|
|
break;
|
|
} /* endswitch */
|
|
|
|
while ( (*str >= '0') && (*str <= '9') ) {
|
|
if (ex_exp < (DBL_MAX_EXP * 2) ) ex_exp = ex_exp * 10 + (*str - '0');
|
|
str++;
|
|
exp_dcnt++;
|
|
} /* endwhile */
|
|
|
|
exp += exp_sign ? -ex_exp : ex_exp;
|
|
} /* endif */
|
|
|
|
if ( radix || exp ) {
|
|
int pow = 256;
|
|
|
|
dblval = msdec;
|
|
if ( msscale != 1 ) dblval = dblval * msscale + lsdec;
|
|
|
|
if ( dblval && exp ) {
|
|
unsigned u = 0;
|
|
|
|
pow = 256;
|
|
while ( exp > 0 ) {
|
|
while ( exp >= pow ) {
|
|
dblval *= pos_table[u];
|
|
exp -= pow;
|
|
} /* endwhile */
|
|
pow >>= 1;
|
|
u++;
|
|
} /* endwhile */
|
|
|
|
while ( exp < 0 ) {
|
|
while ( exp <= -pow ) {
|
|
dblval *= neg_table[u];
|
|
if ( dblval == 0.0 ) {
|
|
errno = ERANGE;
|
|
err = 1;
|
|
} /* endif */
|
|
exp += pow;
|
|
} /* endwhile */
|
|
pow >>= 1;
|
|
u++;
|
|
} /* endwhile */
|
|
|
|
/* if overflow occurred */
|
|
if ( dblval == HUGE_VAL ) {
|
|
errno = ERANGE;
|
|
err = 1;
|
|
} /* endif */
|
|
} /* endif */
|
|
|
|
if ( !err ) {
|
|
rslt = (numeric_ptr)calloc(1,sizeof(numeric));
|
|
rslt->type = DBL_TYPE;
|
|
rslt->value.dbl_value = dblval;
|
|
} /* endif */
|
|
} else {
|
|
if ( (!sign && (inum == LONG_MIN)) || err ) {
|
|
inum = LONG_MIN + sign;
|
|
errno = ERANGE;
|
|
} else {
|
|
rslt = (numeric_ptr)calloc(1,sizeof(numeric));
|
|
rslt->type = INT_TYPE;
|
|
rslt->value.int_value = inum;
|
|
} /* endif */
|
|
} /* endif */
|
|
} /* endif */
|
|
|
|
if ( endstr ) {
|
|
if ( !digit_cnt ) *endstr = (char *) strinit;
|
|
else *endstr = (char *) str;
|
|
} /* endif */
|
|
|
|
return (void *)rslt;
|
|
} /* strtod_flt */
|
|
|
|
/* function to free memory used by numeric structure
|
|
*/
|
|
void free_numeric(
|
|
void *numeric_value)
|
|
{
|
|
if ( numeric_value ) free(numeric_value);
|
|
} /* free_numeric */
|
|
|
|
/* function to perform unary '-' operation
|
|
*/
|
|
void *negate_numeric(
|
|
void *value)
|
|
{
|
|
numeric_ptr rslt = (numeric_ptr)value;
|
|
|
|
switch ( rslt->type ) {
|
|
case INT_TYPE:
|
|
rslt->value.int_value = -rslt->value.int_value;
|
|
break;
|
|
case DBL_TYPE:
|
|
rslt->value.dbl_value = -rslt->value.dbl_value;
|
|
break;
|
|
} /* endswitch */
|
|
|
|
return (void *)rslt;
|
|
} /* negate_numeric */
|
|
|
|
/* function to perform binary operators
|
|
* op_symbol - operation to perform
|
|
* ADD_OP == perform '+'
|
|
* SUB_OP == perform '-'
|
|
* DIV_OP == perform '/'
|
|
* MUL_OP == perform '*'
|
|
* ASN_OP == perform '='
|
|
* l_value - pointer to left hand value
|
|
* r_value - pointer to right hand value
|
|
*/
|
|
void *numeric_ops(
|
|
char op_symbol,
|
|
void *l_value,
|
|
void *r_value)
|
|
{
|
|
numeric_ptr lval = (numeric_ptr)l_value,
|
|
rval = (numeric_ptr)r_value,
|
|
rslt = (op_symbol == ASN_OP) ? lval : (numeric_ptr)calloc(1,sizeof(numeric));
|
|
|
|
switch ( op_symbol ) {
|
|
case ADD_OP:
|
|
if ( lval->type == rval->type ) {
|
|
rslt->type = lval->type;
|
|
switch ( lval->type ) {
|
|
case INT_TYPE:
|
|
rslt->value.int_value = lval->value.int_value + rval->value.int_value;
|
|
break;
|
|
case DBL_TYPE:
|
|
rslt->value.dbl_value = lval->value.dbl_value + rval->value.dbl_value;
|
|
break;
|
|
} /* endswitch */
|
|
} else {
|
|
rslt->type = DBL_TYPE;
|
|
switch ( lval->type ) {
|
|
case INT_TYPE:
|
|
rslt->value.dbl_value = (double)(lval->value.int_value) + rval->value.dbl_value;
|
|
break;
|
|
case DBL_TYPE:
|
|
rslt->value.dbl_value = lval->value.dbl_value + (double)(rval->value.int_value);
|
|
break;
|
|
|
|
} /* endswitch */
|
|
} /* endif */
|
|
break;
|
|
case SUB_OP:
|
|
if ( lval->type == rval->type ) {
|
|
rslt->type = lval->type;
|
|
switch ( lval->type ) {
|
|
case INT_TYPE:
|
|
rslt->value.int_value = lval->value.int_value - rval->value.int_value;
|
|
break;
|
|
case DBL_TYPE:
|
|
rslt->value.dbl_value = lval->value.dbl_value - rval->value.dbl_value;
|
|
break;
|
|
} /* endswitch */
|
|
} else {
|
|
rslt->type = DBL_TYPE;
|
|
switch ( lval->type ) {
|
|
case INT_TYPE:
|
|
rslt->value.dbl_value = (double)(lval->value.int_value) - rval->value.dbl_value;
|
|
break;
|
|
case DBL_TYPE:
|
|
rslt->value.dbl_value = lval->value.dbl_value - (double)(rval->value.int_value);
|
|
break;
|
|
|
|
} /* endswitch */
|
|
} /* endif */
|
|
break;
|
|
case DIV_OP:
|
|
rslt->type = DBL_TYPE;
|
|
if ( lval->type == rval->type ) {
|
|
switch ( lval->type ) {
|
|
case INT_TYPE:
|
|
rslt->value.dbl_value = (double)(lval->value.int_value) / (double)(rval->value.int_value);
|
|
break;
|
|
case DBL_TYPE:
|
|
rslt->value.dbl_value = lval->value.dbl_value / rval->value.dbl_value;
|
|
break;
|
|
} /* endswitch */
|
|
} else {
|
|
switch ( lval->type ) {
|
|
case INT_TYPE:
|
|
rslt->value.dbl_value = (double)(lval->value.int_value) / rval->value.dbl_value;
|
|
break;
|
|
case DBL_TYPE:
|
|
rslt->value.dbl_value = lval->value.dbl_value / (double)(rval->value.int_value);
|
|
break;
|
|
|
|
} /* endswitch */
|
|
} /* endif */
|
|
break;
|
|
case MUL_OP:
|
|
if ( lval->type == rval->type ) {
|
|
rslt->type = lval->type;
|
|
switch ( lval->type ) {
|
|
case INT_TYPE:
|
|
rslt->value.int_value = lval->value.int_value * rval->value.int_value;
|
|
break;
|
|
case DBL_TYPE:
|
|
rslt->value.dbl_value = lval->value.dbl_value * rval->value.dbl_value;
|
|
break;
|
|
} /* endswitch */
|
|
} else {
|
|
rslt->type = DBL_TYPE;
|
|
switch ( lval->type ) {
|
|
case INT_TYPE:
|
|
rslt->value.dbl_value = (double)(lval->value.int_value) * rval->value.dbl_value;
|
|
break;
|
|
case DBL_TYPE:
|
|
rslt->value.dbl_value = lval->value.dbl_value * (double)(rval->value.int_value);
|
|
break;
|
|
|
|
} /* endswitch */
|
|
} /* endif */
|
|
break;
|
|
case ASN_OP:
|
|
if ( !lval ) lval = (numeric_ptr)calloc(1,sizeof(numeric));
|
|
lval->type = rval->type;
|
|
lval->value = rval->value;
|
|
rslt = lval;
|
|
break;
|
|
} /* endswitch */
|
|
|
|
|
|
return (void *)rslt;
|
|
} /* numeric_ops */
|