NumericVar *result, int rscale, bool round);
static void div_var_int(const NumericVar *var, int ival, int ival_weight,
NumericVar *result, int rscale, bool round);
+#ifdef HAVE_INT128
+static void div_var_int64(const NumericVar *var, int64 ival, int ival_weight,
+ NumericVar *result, int rscale, bool round);
+#endif
static int select_div_scale(const NumericVar *var1, const NumericVar *var2);
static void mod_var(const NumericVar *var1, const NumericVar *var2,
NumericVar *result);
/*
* If the divisor has just one or two digits, delegate to div_var_int(),
* which uses fast short division.
+ *
+ * Similarly, on platforms with 128-bit integer support, delegate to
+ * div_var_int64() for divisors with three or four digits.
*/
if (var2ndigits <= 2)
{
div_var_int(var1, idivisor, idivisor_weight, result, rscale, round);
return;
}
+#ifdef HAVE_INT128
+ if (var2ndigits <= 4)
+ {
+ int64 idivisor;
+ int idivisor_weight;
+
+ idivisor = var2->digits[0];
+ idivisor_weight = var2->weight;
+ for (i = 1; i < var2ndigits; i++)
+ {
+ idivisor = idivisor * NBASE + var2->digits[i];
+ idivisor_weight--;
+ }
+ if (var2->sign == NUMERIC_NEG)
+ idivisor = -idivisor;
+
+ div_var_int64(var1, idivisor, idivisor_weight, result, rscale, round);
+ return;
+ }
+#endif
/*
* Otherwise, perform full long division.
/*
* If the divisor has just one or two digits, delegate to div_var_int(),
* which uses fast short division.
+ *
+ * Similarly, on platforms with 128-bit integer support, delegate to
+ * div_var_int64() for divisors with three or four digits.
*/
if (var2ndigits <= 2)
{
div_var_int(var1, idivisor, idivisor_weight, result, rscale, round);
return;
}
+#ifdef HAVE_INT128
+ if (var2ndigits <= 4)
+ {
+ int64 idivisor;
+ int idivisor_weight;
+
+ idivisor = var2->digits[0];
+ idivisor_weight = var2->weight;
+ for (i = 1; i < var2ndigits; i++)
+ {
+ idivisor = idivisor * NBASE + var2->digits[i];
+ idivisor_weight--;
+ }
+ if (var2->sign == NUMERIC_NEG)
+ idivisor = -idivisor;
+
+ div_var_int64(var1, idivisor, idivisor_weight, result, rscale, round);
+ return;
+ }
+#endif
/*
* Otherwise, perform full long division.
}
+#ifdef HAVE_INT128
+/*
+ * div_var_int64() -
+ *
+ * Divide a numeric variable by a 64-bit integer with the specified weight.
+ * The quotient var / (ival * NBASE^ival_weight) is stored in result.
+ *
+ * This duplicates the logic in div_var_int(), so any changes made there
+ * should be made here too.
+ */
+static void
+div_var_int64(const NumericVar *var, int64 ival, int ival_weight,
+ NumericVar *result, int rscale, bool round)
+{
+ NumericDigit *var_digits = var->digits;
+ int var_ndigits = var->ndigits;
+ int res_sign;
+ int res_weight;
+ int res_ndigits;
+ NumericDigit *res_buf;
+ NumericDigit *res_digits;
+ uint64 divisor;
+ int i;
+
+ /* Guard against division by zero */
+ if (ival == 0)
+ ereport(ERROR,
+ errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero"));
+
+ /* Result zero check */
+ if (var_ndigits == 0)
+ {
+ zero_var(result);
+ result->dscale = rscale;
+ return;
+ }
+
+ /*
+ * Determine the result sign, weight and number of digits to calculate.
+ * The weight figured here is correct if the emitted quotient has no
+ * leading zero digits; otherwise strip_var() will fix things up.
+ */
+ if (var->sign == NUMERIC_POS)
+ res_sign = ival > 0 ? NUMERIC_POS : NUMERIC_NEG;
+ else
+ res_sign = ival > 0 ? NUMERIC_NEG : NUMERIC_POS;
+ res_weight = var->weight - ival_weight;
+ /* The number of accurate result digits we need to produce: */
+ res_ndigits = res_weight + 1 + (rscale + DEC_DIGITS - 1) / DEC_DIGITS;
+ /* ... but always at least 1 */
+ res_ndigits = Max(res_ndigits, 1);
+ /* If rounding needed, figure one more digit to ensure correct result */
+ if (round)
+ res_ndigits++;
+
+ res_buf = digitbuf_alloc(res_ndigits + 1);
+ res_buf[0] = 0; /* spare digit for later rounding */
+ res_digits = res_buf + 1;
+
+ /*
+ * Now compute the quotient digits. This is the short division algorithm
+ * described in Knuth volume 2, section 4.3.1 exercise 16, except that we
+ * allow the divisor to exceed the internal base.
+ *
+ * In this algorithm, the carry from one digit to the next is at most
+ * divisor - 1. Therefore, while processing the next digit, carry may
+ * become as large as divisor * NBASE - 1, and so it requires a 128-bit
+ * integer if this exceeds PG_UINT64_MAX.
+ */
+ divisor = i64abs(ival);
+
+ if (divisor <= PG_UINT64_MAX / NBASE)
+ {
+ /* carry cannot overflow 64 bits */
+ uint64 carry = 0;
+
+ for (i = 0; i < res_ndigits; i++)
+ {
+ carry = carry * NBASE + (i < var_ndigits ? var_digits[i] : 0);
+ res_digits[i] = (NumericDigit) (carry / divisor);
+ carry = carry % divisor;
+ }
+ }
+ else
+ {
+ /* carry may exceed 64 bits */
+ uint128 carry = 0;
+
+ for (i = 0; i < res_ndigits; i++)
+ {
+ carry = carry * NBASE + (i < var_ndigits ? var_digits[i] : 0);
+ res_digits[i] = (NumericDigit) (carry / divisor);
+ carry = carry % divisor;
+ }
+ }
+
+ /* Store the quotient in result */
+ digitbuf_free(result->buf);
+ result->ndigits = res_ndigits;
+ result->buf = res_buf;
+ result->digits = res_digits;
+ result->weight = res_weight;
+ result->sign = res_sign;
+
+ /* Round or truncate to target rscale (and set result->dscale) */
+ if (round)
+ round_var(result, rscale);
+ else
+ trunc_var(result, rscale);
+
+ /* Strip leading/trailing zeroes */
+ strip_var(result);
+}
+#endif
+
+
/*
* Default scale selection for division
*