/*************************************************************************** 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 #include #include #include #include #include #include #include #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 */