static NumericVar const_two =
{1, 0, NUMERIC_POS, 0, NULL, const_two_data};
+#if DEC_DIGITS == 4 || DEC_DIGITS == 2
+static NumericDigit const_ten_data[1] = {10};
+static NumericVar const_ten =
+{1, 0, NUMERIC_POS, 0, NULL, const_ten_data};
+#elif DEC_DIGITS == 1
+static NumericDigit const_ten_data[1] = {1};
+static NumericVar const_ten =
+{1, 1, NUMERIC_POS, 0, NULL, const_ten_data};
+#endif
+
#if DEC_DIGITS == 4
static NumericDigit const_zero_point_five_data[1] = {5000};
#elif DEC_DIGITS == 2
static const char *set_var_from_str(const char *str, const char *cp,
NumericVar *dest);
static void set_var_from_num(Numeric value, NumericVar *dest);
+static void init_var_from_num(Numeric num, NumericVar *dest);
static void set_var_from_var(NumericVar *value, NumericVar *dest);
-static char *get_str_from_var(NumericVar *var, int dscale);
+static char *get_str_from_var(NumericVar *var);
static char *get_str_from_var_sci(NumericVar *var, int rscale);
static Numeric make_result(NumericVar *var);
/*
* Get the number in the variable format.
- *
- * Even if we didn't need to change format, we'd still need to copy the
- * value to have a modifiable copy for rounding. set_var_from_num() also
- * guarantees there is extra digit space in case we produce a carry out
- * from rounding.
*/
- init_var(&x);
- set_var_from_num(num, &x);
+ init_var_from_num(num, &x);
- str = get_str_from_var(&x, x.dscale);
-
- free_var(&x);
+ str = get_str_from_var(&x);
PG_RETURN_CSTRING(str);
}
if (NUMERIC_IS_NAN(num))
return pstrdup("NaN");
- init_var(&x);
- set_var_from_num(num, &x);
+ init_var_from_num(num, &x);
str = get_str_from_var_sci(&x, scale);
- free_var(&x);
return str;
}
StringInfoData buf;
int i;
- init_var(&x);
- set_var_from_num(num, &x);
+ init_var_from_num(num, &x);
pq_begintypsend(&buf);
for (i = 0; i < x.ndigits; i++)
pq_sendint(&buf, x.digits[i], sizeof(NumericDigit));
- free_var(&x);
-
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
if (NUMERIC_IS_NAN(num))
PG_RETURN_NUMERIC(make_result(&const_nan));
- init_var(&result);
-
- set_var_from_num(num, &result);
+ init_var_from_num(num, &result);
ceil_var(&result, &result);
res = make_result(&result);
if (NUMERIC_IS_NAN(num))
PG_RETURN_NUMERIC(make_result(&const_nan));
- init_var(&result);
-
- set_var_from_num(num, &result);
+ init_var_from_num(num, &result);
floor_var(&result, &result);
res = make_result(&result);
NumericVar bound2_var;
NumericVar operand_var;
- init_var(&bound1_var);
- init_var(&bound2_var);
- init_var(&operand_var);
-
- set_var_from_num(bound1, &bound1_var);
- set_var_from_num(bound2, &bound2_var);
- set_var_from_num(operand, &operand_var);
+ init_var_from_num(bound1, &bound1_var);
+ init_var_from_num(bound2, &bound2_var);
+ init_var_from_num(operand, &operand_var);
if (cmp_var(&bound1_var, &bound2_var) < 0)
{
/*
* Unpack the values, let add_var() compute the result and return it.
*/
- init_var(&arg1);
- init_var(&arg2);
- init_var(&result);
-
- set_var_from_num(num1, &arg1);
- set_var_from_num(num2, &arg2);
+ init_var_from_num(num1, &arg1);
+ init_var_from_num(num2, &arg2);
+ init_var(&result);
add_var(&arg1, &arg2, &result);
res = make_result(&result);
- free_var(&arg1);
- free_var(&arg2);
free_var(&result);
PG_RETURN_NUMERIC(res);
/*
* Unpack the values, let sub_var() compute the result and return it.
*/
- init_var(&arg1);
- init_var(&arg2);
- init_var(&result);
-
- set_var_from_num(num1, &arg1);
- set_var_from_num(num2, &arg2);
+ init_var_from_num(num1, &arg1);
+ init_var_from_num(num2, &arg2);
+ init_var(&result);
sub_var(&arg1, &arg2, &result);
res = make_result(&result);
- free_var(&arg1);
- free_var(&arg2);
free_var(&result);
PG_RETURN_NUMERIC(res);
* we request exact representation for the product (rscale = sum(dscale of
* arg1, dscale of arg2)).
*/
- init_var(&arg1);
- init_var(&arg2);
- init_var(&result);
-
- set_var_from_num(num1, &arg1);
- set_var_from_num(num2, &arg2);
+ init_var_from_num(num1, &arg1);
+ init_var_from_num(num2, &arg2);
+ init_var(&result);
mul_var(&arg1, &arg2, &result, arg1.dscale + arg2.dscale);
res = make_result(&result);
- free_var(&arg1);
- free_var(&arg2);
free_var(&result);
PG_RETURN_NUMERIC(res);
/*
* Unpack the arguments
*/
- init_var(&arg1);
- init_var(&arg2);
- init_var(&result);
+ init_var_from_num(num1, &arg1);
+ init_var_from_num(num2, &arg2);
- set_var_from_num(num1, &arg1);
- set_var_from_num(num2, &arg2);
+ init_var(&result);
/*
* Select scale for division result
res = make_result(&result);
- free_var(&arg1);
- free_var(&arg2);
free_var(&result);
PG_RETURN_NUMERIC(res);
/*
* Unpack the arguments
*/
- init_var(&arg1);
- init_var(&arg2);
- init_var(&result);
+ init_var_from_num(num1, &arg1);
+ init_var_from_num(num2, &arg2);
- set_var_from_num(num1, &arg1);
- set_var_from_num(num2, &arg2);
+ init_var(&result);
/*
* Do the divide and return the result
res = make_result(&result);
- free_var(&arg1);
- free_var(&arg2);
free_var(&result);
PG_RETURN_NUMERIC(res);
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
PG_RETURN_NUMERIC(make_result(&const_nan));
- init_var(&arg1);
- init_var(&arg2);
- init_var(&result);
+ init_var_from_num(num1, &arg1);
+ init_var_from_num(num2, &arg2);
- set_var_from_num(num1, &arg1);
- set_var_from_num(num2, &arg2);
+ init_var(&result);
mod_var(&arg1, &arg2, &result);
res = make_result(&result);
free_var(&result);
- free_var(&arg2);
- free_var(&arg1);
PG_RETURN_NUMERIC(res);
}
/*
* Compute the result and return it
*/
- init_var(&arg);
-
- set_var_from_num(num, &arg);
+ init_var_from_num(num, &arg);
add_var(&arg, &const_one, &arg);
* to give at least NUMERIC_MIN_SIG_DIGITS significant digits; but in any
* case not less than the input's dscale.
*/
- init_var(&arg);
- init_var(&result);
+ init_var_from_num(num, &arg);
- set_var_from_num(num, &arg);
+ init_var(&result);
/* Assume the input was normalized, so arg.weight is accurate */
sweight = (arg.weight + 1) * DEC_DIGITS / 2 - 1;
res = make_result(&result);
free_var(&result);
- free_var(&arg);
PG_RETURN_NUMERIC(res);
}
* to give at least NUMERIC_MIN_SIG_DIGITS significant digits; but in any
* case not less than the input's dscale.
*/
- init_var(&arg);
- init_var(&result);
+ init_var_from_num(num, &arg);
- set_var_from_num(num, &arg);
+ init_var(&result);
/* convert input to float8, ignoring overflow */
val = numericvar_to_double_no_overflow(&arg);
res = make_result(&result);
free_var(&result);
- free_var(&arg);
PG_RETURN_NUMERIC(res);
}
if (NUMERIC_IS_NAN(num))
PG_RETURN_NUMERIC(make_result(&const_nan));
- init_var(&arg);
+ init_var_from_num(num, &arg);
init_var(&result);
- set_var_from_num(num, &arg);
-
/* Approx decimal digits before decimal point */
dec_digits = (arg.weight + 1) * DEC_DIGITS;
res = make_result(&result);
free_var(&result);
- free_var(&arg);
PG_RETURN_NUMERIC(res);
}
/*
* Initialize things
*/
- init_var(&arg1);
- init_var(&arg2);
+ init_var_from_num(num1, &arg1);
+ init_var_from_num(num2, &arg2);
init_var(&result);
- set_var_from_num(num1, &arg1);
- set_var_from_num(num2, &arg2);
-
/*
* Call log_var() to compute and return the result; note it handles scale
* selection itself.
res = make_result(&result);
free_var(&result);
- free_var(&arg2);
- free_var(&arg1);
PG_RETURN_NUMERIC(res);
}
/*
* Initialize things
*/
- init_var(&arg1);
- init_var(&arg2);
init_var(&arg2_trunc);
init_var(&result);
+ init_var_from_num(num1, &arg1);
+ init_var_from_num(num2, &arg2);
- set_var_from_num(num1, &arg1);
- set_var_from_num(num2, &arg2);
set_var_from_var(&arg2, &arg2_trunc);
-
trunc_var(&arg2_trunc, 0);
/*
res = make_result(&result);
free_var(&result);
- free_var(&arg2);
free_var(&arg2_trunc);
- free_var(&arg1);
PG_RETURN_NUMERIC(res);
}
errmsg("cannot convert NaN to integer")));
/* Convert to variable format, then convert to int4 */
- init_var(&x);
- set_var_from_num(num, &x);
+ init_var_from_num(num, &x);
result = numericvar_to_int4(&x);
- free_var(&x);
PG_RETURN_INT32(result);
}
errmsg("cannot convert NaN to bigint")));
/* Convert to variable format and thence to int8 */
- init_var(&x);
- set_var_from_num(num, &x);
+ init_var_from_num(num, &x);
if (!numericvar_to_int8(&x, &result))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range")));
- free_var(&x);
-
PG_RETURN_INT64(result);
}
errmsg("cannot convert NaN to smallint")));
/* Convert to variable format and thence to int8 */
- init_var(&x);
- set_var_from_num(num, &x);
+ init_var_from_num(num, &x);
if (!numericvar_to_int8(&x, &val))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("smallint out of range")));
- free_var(&x);
-
/* Down-convert to int2 */
result = (int16) val;
if (NUMERIC_IS_NAN(N) || NUMERIC_IS_NAN(sumX) || NUMERIC_IS_NAN(sumX2))
return make_result(&const_nan);
- init_var(&vN);
- set_var_from_num(N, &vN);
+ init_var_from_num(N, &vN);
/*
* Sample stddev and variance are undefined when N <= 1; population stddev
if (cmp_var(&vN, comp) <= 0)
{
- free_var(&vN);
*is_null = true;
return NULL;
}
init_var(&vNminus1);
sub_var(&vN, &const_one, &vNminus1);
- init_var(&vsumX);
- set_var_from_num(sumX, &vsumX);
- init_var(&vsumX2);
- set_var_from_num(sumX2, &vsumX2);
+ init_var_from_num(sumX, &vsumX);
+ init_var_from_num(sumX2, &vsumX2);
/* compute rscale for mul_var calls */
rscale = vsumX.dscale * 2;
res = make_result(&vsumX);
}
- free_var(&vN);
free_var(&vNminus1);
free_var(&vsumX);
free_var(&vsumX2);
}
+/*
+ * init_var_from_num() -
+ *
+ * Initialize a variable from packed db format. The digits array is not
+ * copied, which saves some cycles when the resulting var is not modified.
+ * Also, there's no need to call free_var(), as long as you don't assign any
+ * other value to it (with set_var_* functions, or by using the var as the
+ * destination of a function like add_var())
+ *
+ * CAUTION: Do not modify the digits buffer of a var initialized with this
+ * function, e.g by calling round_var() or trunc_var(), as the changes will
+ * propagate to the original Numeric! It's OK to use it as the destination
+ * argument of one of the calculational functions, though.
+ */
+static void
+init_var_from_num(Numeric num, NumericVar *dest)
+{
+ dest->ndigits = NUMERIC_NDIGITS(num);
+ dest->weight = NUMERIC_WEIGHT(num);
+ dest->sign = NUMERIC_SIGN(num);
+ dest->dscale = NUMERIC_DSCALE(num);
+ dest->digits = NUMERIC_DIGITS(num);
+ dest->buf = NULL; /* digits array is not palloc'd */
+}
+
+
/*
* set_var_from_var() -
*
* get_str_from_var() -
*
* Convert a var to text representation (guts of numeric_out).
- * CAUTION: var's contents may be modified by rounding!
+ * The var is displayed to the number of digits indicated by its dscale.
* Returns a palloc'd string.
*/
static char *
-get_str_from_var(NumericVar *var, int dscale)
+get_str_from_var(NumericVar *var)
{
+ int dscale;
char *str;
char *cp;
char *endcp;
NumericDigit d1;
#endif
- if (dscale < 0)
- dscale = 0;
-
- /*
- * Check if we must round up before printing the value and do so.
- */
- round_var(var, dscale);
+ dscale = var->dscale;
/*
* Allocate space for the result.
* rscale is the number of decimal digits desired after the decimal point in
* the output, negative values will be treated as meaning zero.
*
- * CAUTION: var's contents may be modified by rounding!
- *
* Returns a palloc'd string.
*/
static char *
init_var(&denominator);
init_var(&significand);
- int8_to_numericvar((int64) 10, &denominator);
- power_var_int(&denominator, exponent, &denominator, denom_scale);
+ power_var_int(&const_ten, exponent, &denominator, denom_scale);
div_var(var, &denominator, &significand, rscale, true);
- sig_out = get_str_from_var(&significand, rscale);
+ sig_out = get_str_from_var(&significand);
free_var(&denominator);
free_var(&significand);
* Convert numeric to int8, rounding if needed.
*
* If overflow, return FALSE (no error is raised). Return TRUE if okay.
- *
- * CAUTION: var's contents may be modified by rounding!
*/
static bool
numericvar_to_int8(NumericVar *var, int64 *result)
int64 val,
oldval;
bool neg;
+ NumericVar rounded;
/* Round to nearest integer */
- round_var(var, 0);
+ init_var(&rounded);
+ set_var_from_var(var, &rounded);
+ round_var(&rounded, 0);
/* Check for zero input */
- strip_var(var);
- ndigits = var->ndigits;
+ strip_var(&rounded);
+ ndigits = rounded.ndigits;
if (ndigits == 0)
{
*result = 0;
+ free_var(&rounded);
return true;
}
* For input like 10000000000, we must treat stripped digits as real. So
* the loop assumes there are weight+1 digits before the decimal point.
*/
- weight = var->weight;
+ weight = rounded.weight;
Assert(weight >= 0 && ndigits <= weight + 1);
/* Construct the result */
- digits = var->digits;
- neg = (var->sign == NUMERIC_NEG);
+ digits = rounded.digits;
+ neg = (rounded.sign == NUMERIC_NEG);
val = digits[0];
for (i = 1; i <= weight; i++)
{
if ((val / NBASE) != oldval) /* possible overflow? */
{
if (!neg || (-val) != val || val == 0 || oldval < 0)
+ {
+ free_var(&rounded);
return false;
+ }
}
}
+ free_var(&rounded);
+
*result = neg ? -val : val;
return true;
}
double val;
char *endptr;
- tmp = get_str_from_var(var, var->dscale);
+ tmp = get_str_from_var(var);
/* unlike float8in, we ignore ERANGE from strtod */
val = strtod(tmp, &endptr);
if (exp->ndigits == 0 || exp->ndigits <= exp->weight + 1)
{
/* exact integer, but does it fit in int? */
- NumericVar x;
int64 expval64;
- /* must copy because numericvar_to_int8() scribbles on input */
- init_var(&x);
- set_var_from_var(exp, &x);
- if (numericvar_to_int8(&x, &expval64))
+ if (numericvar_to_int8(exp, &expval64))
{
int expval = (int) expval64;
rscale = Min(rscale, NUMERIC_MAX_DISPLAY_SCALE);
power_var_int(base, expval, result, rscale);
-
- free_var(&x);
return;
}
}
- free_var(&x);
}
/*