453 lines
11 KiB
C
453 lines
11 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2001, 2013 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 "arit.h"
|
|
#include "mvalconv.h"
|
|
#include "gtm_stdio.h" /* this is here due to the need for an SPRINTF,
|
|
* which is in turn due the kudge that is the current double2mval routine
|
|
*/
|
|
/* we return strings for >18 digit 64-bit numbers, so pull in stringpool */
|
|
#include "stringpool.h"
|
|
|
|
GBLREF spdesc stringpool;
|
|
|
|
LITREF int4 ten_pwr[];
|
|
|
|
void i2smval(mval *v, uint4 i)
|
|
{
|
|
char *c;
|
|
int exp;
|
|
int4 n;
|
|
|
|
v->mvtype = MV_NM | MV_STR;
|
|
v->m[1] = n = i;
|
|
v->sgn = 0;
|
|
c = v->str.addr;
|
|
exp = 100000000;
|
|
while (exp && !(n = i / exp))
|
|
exp /= 10;
|
|
if (!exp)
|
|
*c++ = 0;
|
|
else for (;;)
|
|
{
|
|
*c++ = n + '0';
|
|
i -= n * exp;
|
|
exp /= 10;
|
|
if (!exp)
|
|
break;
|
|
n = i / exp;
|
|
}
|
|
if (v->m[1] < INT_HI)
|
|
{
|
|
v->mvtype |= MV_INT;
|
|
v->m[1] = MV_BIAS * i;
|
|
} else if (v->m[1] < MANT_HI)
|
|
{
|
|
v->m[1] *= ten_pwr[NUM_DEC_DG_1L - exp];
|
|
v->m[0] = 0;
|
|
} else
|
|
{
|
|
v->m[0] = (v->m[1] % 10) * MANT_LO;
|
|
v->m[1] /= 10;
|
|
}
|
|
v->str.len = INTCAST(c - v->str.addr);
|
|
v->e = v->str.len + MV_XBIAS;
|
|
assert(v->m[1] < MANT_HI);
|
|
}
|
|
|
|
void xi2mval(mval *v, unsigned int i);
|
|
|
|
void i2usmval(mval *v, unsigned int i)
|
|
{
|
|
v->mvtype = MV_NM;
|
|
v->sgn = 0;
|
|
|
|
xi2mval(v, i);
|
|
}
|
|
|
|
void i2mval(mval *v, int i)
|
|
{
|
|
int4 n;
|
|
|
|
v->mvtype = MV_NM;
|
|
if (i < 0)
|
|
{
|
|
v->sgn = 1;
|
|
n = -i;
|
|
} else
|
|
{
|
|
n = i;
|
|
v->sgn = 0;
|
|
}
|
|
|
|
xi2mval(v, n);
|
|
}
|
|
|
|
/* xi2mval does the bulk of the conversion for i2mval and i2usmval.
|
|
* The primary routines set the sgn flag and pass the absolute value
|
|
* to xi2mval. */
|
|
|
|
void xi2mval(mval *v, unsigned int i)
|
|
{
|
|
int exp;
|
|
|
|
if (i < INT_HI)
|
|
{
|
|
v->mvtype |= MV_INT;
|
|
v->m[1] = MV_BIAS * (v->sgn ? -(int)i : i);
|
|
} else
|
|
{
|
|
if (i < MANT_HI)
|
|
{
|
|
for (exp = EXP_IDX_BIAL; i < MANT_LO; exp--)
|
|
i *= 10;
|
|
v->e = exp;
|
|
v->m[0] = 0;
|
|
v->m[1] = i;
|
|
} else
|
|
{
|
|
v->m[0] = (i % 10) * MANT_LO;
|
|
v->m[1] = i / 10;
|
|
v->e = EXP_IDX_BIAL + 1;
|
|
}
|
|
assert(v->m[1] < MANT_HI);
|
|
assert(v->m[1] >= MANT_LO);
|
|
}
|
|
}
|
|
|
|
void xi82mval(mval *v, gtm_uint64_t i);
|
|
|
|
void ui82mval(mval *v, gtm_uint64_t i)
|
|
{
|
|
v->mvtype = MV_NM;
|
|
v->sgn = 0;
|
|
|
|
xi82mval(v, i);
|
|
}
|
|
|
|
void i82mval(mval *v, gtm_int64_t i)
|
|
{
|
|
gtm_uint64_t absi;
|
|
|
|
v->mvtype = MV_NM;
|
|
if (i < 0)
|
|
{
|
|
v->sgn = 1;
|
|
absi = -i;
|
|
} else
|
|
{
|
|
v->sgn = 0;
|
|
absi = i;
|
|
}
|
|
|
|
xi82mval(v, absi);
|
|
}
|
|
|
|
/* This function does the bulk of the conversion for i82mval and ui82mval. The primary routines set the sgn flag and pass the
|
|
* absolute value to xi82mval(). In the case of a >18 digit number, xi82mval() examines the sgn flag to determine whether to
|
|
* switch back to a signed value before string conversion.
|
|
*/
|
|
void xi82mval(mval *v, gtm_uint64_t i)
|
|
{
|
|
int exp;
|
|
uint4 low;
|
|
uint4 high;
|
|
char buf[21]; /* [possible] sign, [up to] 19L/20UL digits, and terminator. */
|
|
int len;
|
|
|
|
if (i < INT_HI)
|
|
{
|
|
v->mvtype |= MV_INT;
|
|
v->m[1] = MV_BIAS * (v->sgn ? -(int4)i : (uint4)i);
|
|
} else
|
|
{
|
|
if (i < MANT_HI)
|
|
{
|
|
low = 0;
|
|
high = i;
|
|
exp = EXP_IDX_BIAL;
|
|
while (high < MANT_LO)
|
|
{
|
|
high *= 10;
|
|
exp--;
|
|
}
|
|
v->e = exp;
|
|
v->m[0] = low;
|
|
v->m[1] = high;
|
|
} else if (i < (gtm_uint64_t)MANT_HI * MANT_HI)
|
|
{
|
|
low = i % MANT_HI;
|
|
high = i / MANT_HI;
|
|
exp = EXP_IDX_BIAL + 9;
|
|
while (high < MANT_LO)
|
|
{
|
|
high = (high * 10) + (low / MANT_LO);
|
|
low = (low % MANT_LO) * 10;
|
|
exp--;
|
|
}
|
|
v->e = exp;
|
|
v->m[0] = low;
|
|
v->m[1] = high;
|
|
} else
|
|
{ /* The value won't fit in 18 digits, so return a string. */
|
|
if (v->sgn)
|
|
len = SPRINTF(buf, "%lld", -(gtm_int64_t)i);
|
|
else
|
|
len = SPRINTF(buf, "%llu", i);
|
|
assert(18 < len);
|
|
ENSURE_STP_FREE_SPACE(len);
|
|
memcpy(stringpool.free, buf, len);
|
|
v->mvtype = MV_STR;
|
|
v->str.len = len;
|
|
v->str.addr = (char *)stringpool.free;
|
|
stringpool.free += len;
|
|
}
|
|
assert((v->mvtype != MV_NM) || (v->m[1] < MANT_HI));
|
|
assert((v->mvtype != MV_NM) || (v->m[1] >= MANT_LO));
|
|
}
|
|
}
|
|
|
|
double mval2double(mval *v)
|
|
{
|
|
double x, y;
|
|
int exp;
|
|
|
|
MV_FORCE_NUM(v);
|
|
x = v->m[1];
|
|
if (v->mvtype & MV_INT)
|
|
x /= MV_BIAS;
|
|
else
|
|
{
|
|
exp = v->e;
|
|
y = v->m[0];
|
|
y = y / MANT_HI;
|
|
while (exp > EXP_IDX_BIAL)
|
|
{
|
|
x *= MANT_HI;
|
|
y *= MANT_HI;
|
|
exp -= 9;
|
|
}
|
|
while (exp < MV_XBIAS)
|
|
{
|
|
x /= MANT_HI;
|
|
y /= MANT_HI;
|
|
exp += 9;
|
|
}
|
|
x /= ten_pwr[EXP_IDX_BIAL - exp];
|
|
y /= ten_pwr[EXP_IDX_BIAL - exp];
|
|
x += y;
|
|
x = (v->sgn ? -x : x);
|
|
}
|
|
return x;
|
|
}
|
|
|
|
void float2mval(mval *dst, float src)
|
|
{
|
|
char buf[16]; /* The maximum-length value in the 'F' representation would look like -0.123457 (that is,
|
|
* sign[1] + zero[1] + dot[1] + digits[6] = 9). Note that for values below -1 the number would
|
|
* be shorter, since the integer part would be counted against the precision capacity. However,
|
|
* a longer representation is possible in the 'E' format: -1.23456E+12 (that is, sign[1] +
|
|
* digit[1] + dot[1] + digits[5] + E[1] + sign[1] + exp[2] = 12). Although we only need to
|
|
* additionally worry about one termination character, we will be safe and allocate 16 bytes
|
|
* instead of 13. */
|
|
|
|
SPRINTF(buf, "%.6G", src);
|
|
dst->mvtype = MV_STR;
|
|
dst->str.len = STRLEN(buf);
|
|
dst->str.addr = buf;
|
|
s2n(dst);
|
|
dst->mvtype &= ~MV_STR;
|
|
return;
|
|
}
|
|
|
|
void double2mval(mval *dst, double src)
|
|
{
|
|
char buf[32]; /* The maximum-length value in the 'F' representation would look like -0.123456789012345
|
|
* (that is, sign[1] + zero[1] + dot[1] + digits[15] = 18). Note that for values below -1
|
|
* the number would be shorter, since the integer part would be counted against the
|
|
* precision capacity. However, a longer representation is possible in the 'E' format:
|
|
* -1.234567890123456E+123 (that is, sign[1] + digit[1] + dot[1] + digits[14] + E[1] +
|
|
* sign[1] + exp[3] = 23). Although we only need to additionally worry about one termination
|
|
* character, we will be safe and allocate 32 bytes instead of 24. */
|
|
|
|
SPRINTF(buf, "%.15G", src);
|
|
dst->mvtype = MV_STR;
|
|
dst->str.len = STRLEN(buf);
|
|
dst->str.addr = buf;
|
|
s2n(dst);
|
|
dst->mvtype &= ~MV_STR;
|
|
return;
|
|
}
|
|
|
|
/* Converts an mval into a 32-bit signed integer, or MAXPOSINT4 on overflow. */
|
|
int4 mval2i(mval *v)
|
|
{
|
|
int4 i;
|
|
double j;
|
|
int exp;
|
|
|
|
MV_FORCE_NUM(v);
|
|
if (v->mvtype & MV_INT)
|
|
i = v->m[1] / MV_BIAS;
|
|
else
|
|
{
|
|
exp = v->e;
|
|
if (exp > EXP_IDX_BIAL)
|
|
{
|
|
j = mval2double(v);
|
|
i = (MAXPOSINT4 >= j) ? (int4)j : MAXPOSINT4;
|
|
} else if (exp < MV_XBIAS)
|
|
i = 0;
|
|
else
|
|
i = (v->sgn ? -v->m[1] : v->m[1]) / ten_pwr[EXP_IDX_BIAL - exp];
|
|
}
|
|
return i;
|
|
}
|
|
|
|
/* Converts an mval into a 32-bit unsigned integer, or MAXUINT4 on overflow. */
|
|
uint4 mval2ui(mval *v)
|
|
{
|
|
uint4 i;
|
|
double j;
|
|
int exp;
|
|
|
|
MV_FORCE_NUM(v);
|
|
if (v->mvtype & MV_INT)
|
|
i = v->m[1] / MV_BIAS;
|
|
else
|
|
{
|
|
exp = v->e;
|
|
if (exp > EXP_IDX_BIAL)
|
|
{
|
|
j = mval2double(v);
|
|
i = (MAXUINT4 >= j) ? (uint4)j : MAXUINT4;
|
|
} else if (exp < MV_XBIAS)
|
|
i = 0;
|
|
else
|
|
i = (v->sgn ? -v->m[1] : v->m[1]) / ten_pwr[EXP_IDX_BIAL - exp];
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
/* Converts an mval into a 64-bit unsigned integer. */
|
|
gtm_uint64_t mval2ui8(mval *v)
|
|
{
|
|
return (gtm_uint64_t)mval2i8(v);
|
|
}
|
|
|
|
gtm_int64_t mval2i8(mval *v)
|
|
{
|
|
gtm_int64_t x, y;
|
|
int exp;
|
|
|
|
MV_FORCE_NUM(v);
|
|
if (v->mvtype & MV_INT)
|
|
x = v->m[1] / MV_BIAS;
|
|
else
|
|
{
|
|
exp = v->e;
|
|
if (exp > EXP_IDX_BIAL)
|
|
{ /* Case where to get the actual value we need to multiply by power of exponent. */
|
|
x = v->m[1];
|
|
y = v->m[0];
|
|
if (y > 0)
|
|
{ /* Both m[0] and m[1] are used, so multiply in parallel, but first ensure that the m[1] part has
|
|
* a decimal exponent of MANT_HI order.
|
|
*/
|
|
x *= MANT_HI;
|
|
while (exp > EXP_IDX_BIAL + 18)
|
|
{ /* Keep multiplying by 10^9, but keep a precision "buffer" of 18 to prevent further
|
|
* divisions, as we might otherwise compromise the available precision of mval.
|
|
*/
|
|
x *= MANT_HI;
|
|
y *= MANT_HI;
|
|
exp -= 9;
|
|
}
|
|
if (exp >= EXP_IDX_BIAL + 9)
|
|
{ /* Multiply by the remaining power of the exponent. */
|
|
x *= ten_pwr[exp - EXP_IDX_BIAL - 9];
|
|
y *= ten_pwr[exp - EXP_IDX_BIAL - 9];
|
|
} else
|
|
{ /* Case where exponent indicates a total power of less than 10^9, which, given that both
|
|
* m[0] and m[1] are used and that x has already been multiplied by 10^9, requires a
|
|
* division to make the sum of m[0] and m[1] represent the right number.
|
|
*/
|
|
x /= ten_pwr[EXP_IDX_BIAL + 9 - exp];
|
|
y /= ten_pwr[EXP_IDX_BIAL + 9 - exp];
|
|
}
|
|
} else
|
|
{ /* Since m[0] is not used, just multiply x by the excess power of the exponent. */
|
|
while (exp > EXP_IDX_BIAL + 9)
|
|
{
|
|
x *= MANT_HI;
|
|
exp -= 9;
|
|
}
|
|
x *= ten_pwr[exp - EXP_IDX_BIAL];
|
|
}
|
|
|
|
x = (v->sgn ? -x - y : x + y);
|
|
} else if (exp < MV_XBIAS)
|
|
x = 0;
|
|
else
|
|
x = (v->sgn ? -v->m[1] : v->m[1]) / ten_pwr[EXP_IDX_BIAL - exp];
|
|
}
|
|
return x;
|
|
}
|
|
|
|
/* isint == v can be represented as a 9 digit (or less) integer (positive or negative).
|
|
* If return value is TRUE, then "*intval" contains the integer value stored in "v".
|
|
* Note: "*intval" could have been updated even if return value is FALSE.
|
|
*/
|
|
boolean_t isint(mval *v, int4 *intval)
|
|
{
|
|
int exp, m1, mvtype, divisor, m1_div;
|
|
DEBUG_ONLY(boolean_t is_canonical;)
|
|
|
|
mvtype = v->mvtype;
|
|
/* Note that input mval might have "MV_NM" bit set even though it is not a numeric (i.e. a string).
|
|
* This is possible in case the input mval is a constant literal string. In this case, since these
|
|
* might reside in read-only sections of the executable and the MV_FORCE_* macros might not be able
|
|
* to touch them, we define the numeric portions of the mval to be 0 and set the MV_NM bit as well.
|
|
* But in addition, the MV_NUM_APPROX bit will be set to indicate this is an approximation. So if we
|
|
* see the MV_NM bit set, we should also check the MV_NUM_APPROX bit is unset before we go ahead
|
|
* and check the numeric part of this mval for whether it is an integer.
|
|
*/
|
|
DEBUG_ONLY(is_canonical = MV_IS_CANONICAL(v));
|
|
assert(!is_canonical || (MVTYPE_IS_NUMERIC(mvtype) && !MVTYPE_IS_NUM_APPROX(mvtype)));
|
|
if (!MVTYPE_IS_NUMERIC(mvtype) || MVTYPE_IS_NUM_APPROX(mvtype))
|
|
return FALSE;
|
|
assert(v->m[1] < MANT_HI);
|
|
if (mvtype & MV_INT)
|
|
{
|
|
divisor = MV_BIAS;
|
|
m1 = v->m[1];
|
|
} else
|
|
{
|
|
exp = v->e;
|
|
if ((MV_XBIAS >= exp) || (EXP_IDX_BIAL < exp) || (0 != v->m[0]))
|
|
return FALSE;
|
|
divisor = ten_pwr[EXP_IDX_BIAL - exp];
|
|
if (v->sgn)
|
|
m1 = -v->m[1];
|
|
else
|
|
m1 = v->m[1];
|
|
}
|
|
m1_div = (m1 / divisor);
|
|
assert(NULL != intval);
|
|
*intval = m1_div;
|
|
return ((m1_div * divisor) == m1);
|
|
}
|