315 lines
8.6 KiB
C
315 lines
8.6 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2001, 2012 Fidelity Information Services, Inc *
|
|
* *
|
|
* This source code contains the intellectual property *
|
|
* of its copyright holder(s), and is made available *
|
|
* under a license. If you do not know the terms of *
|
|
* the license, please stop and do not read further. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#include "mdef.h"
|
|
|
|
#include "gtm_string.h"
|
|
#include <math.h>
|
|
|
|
#include "arit.h"
|
|
#include "stringpool.h"
|
|
#include "op.h"
|
|
#include "mvalconv.h"
|
|
|
|
#define ACCUR_PERCENT 0.00000000000000055
|
|
#define MAX_M_INT 999999999
|
|
|
|
LITREF int4 ten_pwr[];
|
|
LITREF mval literal_one;
|
|
LITREF mval literal_zero;
|
|
|
|
STATICFNDEF void op_exp_flgchk(mval *mv);
|
|
|
|
error_def(ERR_NEGFRACPWR);
|
|
error_def(ERR_NUMOFLOW);
|
|
|
|
void op_exp(mval *u, mval* v, mval *p)
|
|
{
|
|
mval u1, *u1_p;
|
|
double accuracy, exponent;
|
|
double x, x1, y, z, z2, z3, z4, z5, id, il;
|
|
int im0, im1, ie, i, j, j1;
|
|
boolean_t fraction = FALSE, in = FALSE;
|
|
boolean_t neg = FALSE, change_sn = FALSE, even = TRUE;
|
|
mval w, zmv;
|
|
int4 n, n1;
|
|
int4 z1_rnd, z2_rnd, pten;
|
|
|
|
u1_p = &u1;
|
|
memcpy(u1_p, u, SIZEOF(mval));
|
|
MV_FORCE_NUM(u1_p);
|
|
MV_FORCE_NUM(v);
|
|
if ((0 == v->m[1]) && (0 == v->m[0]))
|
|
{ /* 0**0 = 1 */
|
|
*p = literal_one;
|
|
return;
|
|
}
|
|
if (0 != (v->mvtype & MV_INT))
|
|
{ /* Integer-ish exponent (could have up to 3 digits to right of decimal pt) */
|
|
n = v->m[1];
|
|
if (0 == n)
|
|
{ /* anything**0 = 1 where anything != 0 */
|
|
*p = literal_one;
|
|
return;
|
|
}
|
|
if (0 != (u1_p->mvtype & MV_INT))
|
|
{ /* Integer-ish base */
|
|
if (0 == u1_p->m[1])
|
|
{ /* 0**anything = 0 */
|
|
*p = literal_zero;
|
|
return;
|
|
}
|
|
} else if ((0 == u1_p->m[1]) && (0 == u1_p->m[0]))
|
|
{ /* 0**anything = 0 */
|
|
*p = literal_zero;
|
|
return;
|
|
}
|
|
n1 = n / MV_BIAS;
|
|
if ((n1 * MV_BIAS) == n)
|
|
{ /* True non-fractional exponent */
|
|
if (0 == v->m[1])
|
|
{ /* Duplicate of check on line 63? */
|
|
*p = literal_one;
|
|
return;
|
|
}
|
|
if (0 > n1)
|
|
{ /* Create inverse due to negative exponent */
|
|
op_div((mval *)&literal_one, u1_p, &w);
|
|
n1 = -n1;
|
|
} else
|
|
w = *u1_p;
|
|
zmv = literal_one;
|
|
for ( ; ; )
|
|
{ /* Compute integer exponent */
|
|
if (n1 & 1)
|
|
op_mul(&zmv, &w, &zmv);
|
|
n1 /= 2;
|
|
if (!n1)
|
|
break;
|
|
op_mul(&w, &w, &w);
|
|
}
|
|
*p = zmv;
|
|
return;
|
|
} else
|
|
{ /* Have non-integer exponent (has fractional component) */
|
|
if (0 != (u1_p->mvtype & MV_INT))
|
|
{ /* Base is integer-ish */
|
|
if (0 > u1_p->m[1])
|
|
{ /* Base is negative, invalid exponent expression */
|
|
rts_error(VARLSTCNT(1) ERR_NEGFRACPWR);
|
|
return;
|
|
}
|
|
} else
|
|
{ /* Base is NOT integer-sh */
|
|
if (u1_p->sgn)
|
|
{ /* Base is negative, invalid exponent expression */
|
|
rts_error(VARLSTCNT(1) ERR_NEGFRACPWR);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
{ /* Exponent NOT integer-ish */
|
|
if (0 != (u1_p->mvtype & MV_INT))
|
|
{ /* Base is integer-ish */
|
|
if (0 > u1_p->m[1])
|
|
{ /* Base is negative - make positive but record was negative */
|
|
u1_p->m[1] = -u1_p->m[1];
|
|
neg = TRUE;
|
|
}
|
|
if (0 == u1_p->m[1])
|
|
{ /* 0**anything is 0 */
|
|
*p = literal_zero;
|
|
return;
|
|
}
|
|
} else if (u1_p->sgn)
|
|
{ /* Base is NOT integer-ish and is negative - clear sign and record with flag */
|
|
u1_p->sgn = 0;
|
|
neg = TRUE;
|
|
if ((0 == u1_p->m[1]) && (0 == u1_p->m[0]))
|
|
{ /* 0**anything is zero */
|
|
*p = literal_zero;
|
|
return;
|
|
}
|
|
}
|
|
if (NUM_DEC_DG_2L > (ie = (v->e - MV_XBIAS))) /* Note assignment */
|
|
{ /* Need to determine 2 things:
|
|
* 1. Whether this exponent has a fractional part (vs just being large)
|
|
* 2. If the base value is negative, result is negative based on whether exponent is even/odd
|
|
* Since all exponent digits are visible, determine these items.
|
|
*/
|
|
if (0 < ie)
|
|
{ /* Decimal point shifting to right - see if fully integer*/
|
|
if (NUM_DEC_DG_1L > ie)
|
|
{ /* Exponent fits in int4 */
|
|
for (i = 1, j = 10; (NUM_DEC_DG_1L - ie) > i; j *= 10, i++)
|
|
; /* Determine exponential scale */
|
|
im1 = v->m[1];
|
|
if (0 != (i = im1 % j)) /* Note assignment */
|
|
fraction = TRUE;
|
|
else
|
|
if (0 != (i = im1 % (2 * j)))
|
|
even = FALSE;
|
|
} else
|
|
{
|
|
im0 = v->m[0];
|
|
if (NUM_DEC_DG_1L == ie)
|
|
{ /* Var ie is at max for no loss in single int */
|
|
if (0 == im0)
|
|
{
|
|
im1 = v->m[1];
|
|
if (0 != (i = im1 % 2)) /* Note assignment */
|
|
even = FALSE;
|
|
} else
|
|
fraction = TRUE;
|
|
} else
|
|
{
|
|
for (i=1, j=10; (NUM_DEC_DG_2L - ie) > i; j *= 10, i++)
|
|
;
|
|
if (0 == (i = im0 % j)) /* Note assignment */
|
|
{
|
|
if (0 != (i = im0 % (2 * j))) /* Note assignment */
|
|
even = FALSE;
|
|
} else
|
|
fraction = TRUE;
|
|
}
|
|
}
|
|
} else /* Var ie is negative (fraction) or 0. This is a non-integerish number so ie
|
|
* must be a fraction. ie must be > 0 to be non-integer.
|
|
*/
|
|
fraction = TRUE;
|
|
} else
|
|
{
|
|
if (NUM_DEC_DG_2L == ie)
|
|
{ /* Var ie is max to have no significant digit loss */
|
|
im0 = v->m[0];
|
|
if (0 != (i = im0 % 2)) /* Note assignment */
|
|
even = FALSE;
|
|
}
|
|
}
|
|
if (!fraction)
|
|
{ /* Exponent is not fractional */
|
|
if (neg && !even)
|
|
/* Base is negative and non-even exponent means sign needs to change in final result */
|
|
change_sn = TRUE;
|
|
} else
|
|
{ /* Have fractional exponent */
|
|
if (neg)
|
|
{ /* Negative fractional exponent not valid */
|
|
rts_error(VARLSTCNT(1) ERR_NEGFRACPWR);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
x = mval2double(u1_p); /* Convert base and exponent to double for feeding to pow*() function */
|
|
y = mval2double(v);
|
|
z = pow(x, y);
|
|
# ifdef UNIX
|
|
if (HUGE_VAL == z) /* Infinity return value check */
|
|
{
|
|
rts_error(VARLSTCNT(1) ERR_NUMOFLOW);
|
|
return;
|
|
}
|
|
# endif
|
|
assert(!neg); /* Should be taken care of in one of the op_mul() using sections dealing with whole exponents */
|
|
if (0 == z)
|
|
{
|
|
*p = literal_zero;
|
|
return;
|
|
}
|
|
/* Remaining code's main purpose is to convert the double float value to our internal format taking care that
|
|
* actual integer values (e.g. 4**.5 is 2) are correctly rendered. This can be tricky not only because floating
|
|
* point values are often imprecise (e.g. 3.0 represented as 2.999999999999999) but because the double-float
|
|
* type has fewer digits of accuracy than GTM carries.
|
|
*/
|
|
accuracy = z * ACCUR_PERCENT;
|
|
/* Scale accuracy up in case it turned to zero above */
|
|
for (z2 = ACCUR_PERCENT; (0.0 == accuracy); z2 *= 10, accuracy = z * z2)
|
|
;
|
|
if ((fabs(z) <= accuracy) || (0 >= z))
|
|
{ /* Zero equivalency test - some small chance pow() could return a very small negative number. Any
|
|
* possible negative numbers must have whole number exponent which is handled earlier via op_mul().
|
|
*/
|
|
*p = literal_zero;
|
|
return;
|
|
}
|
|
/* Integer check - GT.M pseudo int MV_INT check - must be decimal form 999999.999 */
|
|
z2 = floor((z + .0005) * MV_BIAS);
|
|
if (MANT_HI > z2)
|
|
{ /* Have proper range, check if accuracy warrants conversion */
|
|
n1 = (int)z2;
|
|
z2 = (double)n1 / MV_BIAS;
|
|
if (fabs(z - z2) < accuracy)
|
|
{ /* We can treat this as a GT.M int */
|
|
((mval_b *)p)->sgne = 0;
|
|
p->mvtype = (MV_NM | MV_INT);
|
|
p->m[0] = 0;
|
|
p->m[1] = (p->sgn) ? -n1 : n1;
|
|
return;
|
|
}
|
|
}
|
|
/* Store as a type-2 non-'integer', i.e. put z in the form z1_rnd * 10^n + z2_rnd * 10^(n-9).
|
|
* Could add checks for zero/infinity here to avoid lengthy (300ish iterations) while loops below.
|
|
*/
|
|
n = 0;
|
|
while (1e16 <= z)
|
|
{
|
|
n += 5;
|
|
z *= 1e-5;
|
|
}
|
|
while (1e1 > z)
|
|
{
|
|
n -= 5;
|
|
z *= 1e5;
|
|
}
|
|
while (1e9 <= z)
|
|
{
|
|
n++;
|
|
z *= .1;
|
|
}
|
|
while (1e8 > z)
|
|
{
|
|
n--;
|
|
z *= 10.0;
|
|
}
|
|
z1_rnd = (int)z; /* casts should keep z1_rnd and z2_rnd in proper range, hence the assert below */
|
|
z -= (double)z1_rnd;
|
|
z *= 1e9;
|
|
z2_rnd = (int)z;
|
|
assert(((MANT_LO <= z1_rnd) && (MANT_HI > z1_rnd)) && ((0 <= z2_rnd) && (MANT_HI > z2_rnd)));
|
|
if ((0 == z2_rnd) && (-11 <= n) && (-3 >= n) && (0 == (z1_rnd % ten_pwr[-3 - n])))
|
|
{ /* This is a second-chance at detection an integer in case something slipped through the earlier
|
|
* check. Not expecting it to ever be invoked but is here as a safety net.
|
|
*/
|
|
z1_rnd /= ten_pwr[-3-n];
|
|
((mval_b *)p)->sgne = 0;
|
|
p->mvtype = (MV_NM | MV_INT);
|
|
p->m[1] = z1_rnd;
|
|
return;
|
|
}
|
|
exponent = MV_XBIAS + n + 9;
|
|
if (exponent >= EXPHI)
|
|
{
|
|
rts_error(VARLSTCNT(1) ERR_NUMOFLOW);
|
|
return;
|
|
}
|
|
if (exponent < EXPLO)
|
|
{
|
|
*p = literal_zero;
|
|
return;
|
|
}
|
|
p->mvtype = MV_NM;
|
|
p->e = exponent;
|
|
p->m[1] = z1_rnd;
|
|
p->m[0] = z2_rnd;
|
|
return;
|
|
}
|