@@ -554,6 +554,10 @@ static void div_var_fast(const NumericVar *var1, const NumericVar *var2,
554554 NumericVar * result , int rscale , bool round );
555555static void div_var_int (const NumericVar * var , int ival , int ival_weight ,
556556 NumericVar * result , int rscale , bool round );
557+ #ifdef HAVE_INT128
558+ static void div_var_int64 (const NumericVar * var , int64 ival , int ival_weight ,
559+ NumericVar * result , int rscale , bool round );
560+ #endif
557561static int select_div_scale (const NumericVar * var1 , const NumericVar * var2 );
558562static void mod_var (const NumericVar * var1 , const NumericVar * var2 ,
559563 NumericVar * result );
@@ -8484,6 +8488,9 @@ div_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
84848488 /*
84858489 * If the divisor has just one or two digits, delegate to div_var_int(),
84868490 * which uses fast short division.
8491+ *
8492+ * Similarly, on platforms with 128-bit integer support, delegate to
8493+ * div_var_int64() for divisors with three or four digits.
84878494 */
84888495 if (var2ndigits <= 2 )
84898496 {
@@ -8503,6 +8510,26 @@ div_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
85038510 div_var_int (var1 , idivisor , idivisor_weight , result , rscale , round );
85048511 return ;
85058512 }
8513+ #ifdef HAVE_INT128
8514+ if (var2ndigits <= 4 )
8515+ {
8516+ int64 idivisor ;
8517+ int idivisor_weight ;
8518+
8519+ idivisor = var2 -> digits [0 ];
8520+ idivisor_weight = var2 -> weight ;
8521+ for (i = 1 ; i < var2ndigits ; i ++ )
8522+ {
8523+ idivisor = idivisor * NBASE + var2 -> digits [i ];
8524+ idivisor_weight -- ;
8525+ }
8526+ if (var2 -> sign == NUMERIC_NEG )
8527+ idivisor = - idivisor ;
8528+
8529+ div_var_int64 (var1 , idivisor , idivisor_weight , result , rscale , round );
8530+ return ;
8531+ }
8532+ #endif
85068533
85078534 /*
85088535 * Otherwise, perform full long division.
@@ -8774,6 +8801,9 @@ div_var_fast(const NumericVar *var1, const NumericVar *var2,
87748801 /*
87758802 * If the divisor has just one or two digits, delegate to div_var_int(),
87768803 * which uses fast short division.
8804+ *
8805+ * Similarly, on platforms with 128-bit integer support, delegate to
8806+ * div_var_int64() for divisors with three or four digits.
87778807 */
87788808 if (var2ndigits <= 2 )
87798809 {
@@ -8793,6 +8823,26 @@ div_var_fast(const NumericVar *var1, const NumericVar *var2,
87938823 div_var_int (var1 , idivisor , idivisor_weight , result , rscale , round );
87948824 return ;
87958825 }
8826+ #ifdef HAVE_INT128
8827+ if (var2ndigits <= 4 )
8828+ {
8829+ int64 idivisor ;
8830+ int idivisor_weight ;
8831+
8832+ idivisor = var2 -> digits [0 ];
8833+ idivisor_weight = var2 -> weight ;
8834+ for (i = 1 ; i < var2ndigits ; i ++ )
8835+ {
8836+ idivisor = idivisor * NBASE + var2 -> digits [i ];
8837+ idivisor_weight -- ;
8838+ }
8839+ if (var2 -> sign == NUMERIC_NEG )
8840+ idivisor = - idivisor ;
8841+
8842+ div_var_int64 (var1 , idivisor , idivisor_weight , result , rscale , round );
8843+ return ;
8844+ }
8845+ #endif
87968846
87978847 /*
87988848 * Otherwise, perform full long division.
@@ -9182,6 +9232,123 @@ div_var_int(const NumericVar *var, int ival, int ival_weight,
91829232}
91839233
91849234
9235+ #ifdef HAVE_INT128
9236+ /*
9237+ * div_var_int64() -
9238+ *
9239+ * Divide a numeric variable by a 64-bit integer with the specified weight.
9240+ * The quotient var / (ival * NBASE^ival_weight) is stored in result.
9241+ *
9242+ * This duplicates the logic in div_var_int(), so any changes made there
9243+ * should be made here too.
9244+ */
9245+ static void
9246+ div_var_int64 (const NumericVar * var , int64 ival , int ival_weight ,
9247+ NumericVar * result , int rscale , bool round )
9248+ {
9249+ NumericDigit * var_digits = var -> digits ;
9250+ int var_ndigits = var -> ndigits ;
9251+ int res_sign ;
9252+ int res_weight ;
9253+ int res_ndigits ;
9254+ NumericDigit * res_buf ;
9255+ NumericDigit * res_digits ;
9256+ uint64 divisor ;
9257+ int i ;
9258+
9259+ /* Guard against division by zero */
9260+ if (ival == 0 )
9261+ ereport (ERROR ,
9262+ errcode (ERRCODE_DIVISION_BY_ZERO ),
9263+ errmsg ("division by zero" ));
9264+
9265+ /* Result zero check */
9266+ if (var_ndigits == 0 )
9267+ {
9268+ zero_var (result );
9269+ result -> dscale = rscale ;
9270+ return ;
9271+ }
9272+
9273+ /*
9274+ * Determine the result sign, weight and number of digits to calculate.
9275+ * The weight figured here is correct if the emitted quotient has no
9276+ * leading zero digits; otherwise strip_var() will fix things up.
9277+ */
9278+ if (var -> sign == NUMERIC_POS )
9279+ res_sign = ival > 0 ? NUMERIC_POS : NUMERIC_NEG ;
9280+ else
9281+ res_sign = ival > 0 ? NUMERIC_NEG : NUMERIC_POS ;
9282+ res_weight = var -> weight - ival_weight ;
9283+ /* The number of accurate result digits we need to produce: */
9284+ res_ndigits = res_weight + 1 + (rscale + DEC_DIGITS - 1 ) / DEC_DIGITS ;
9285+ /* ... but always at least 1 */
9286+ res_ndigits = Max (res_ndigits , 1 );
9287+ /* If rounding needed, figure one more digit to ensure correct result */
9288+ if (round )
9289+ res_ndigits ++ ;
9290+
9291+ res_buf = digitbuf_alloc (res_ndigits + 1 );
9292+ res_buf [0 ] = 0 ; /* spare digit for later rounding */
9293+ res_digits = res_buf + 1 ;
9294+
9295+ /*
9296+ * Now compute the quotient digits. This is the short division algorithm
9297+ * described in Knuth volume 2, section 4.3.1 exercise 16, except that we
9298+ * allow the divisor to exceed the internal base.
9299+ *
9300+ * In this algorithm, the carry from one digit to the next is at most
9301+ * divisor - 1. Therefore, while processing the next digit, carry may
9302+ * become as large as divisor * NBASE - 1, and so it requires a 128-bit
9303+ * integer if this exceeds PG_UINT64_MAX.
9304+ */
9305+ divisor = i64abs (ival );
9306+
9307+ if (divisor <= PG_UINT64_MAX / NBASE )
9308+ {
9309+ /* carry cannot overflow 64 bits */
9310+ uint64 carry = 0 ;
9311+
9312+ for (i = 0 ; i < res_ndigits ; i ++ )
9313+ {
9314+ carry = carry * NBASE + (i < var_ndigits ? var_digits [i ] : 0 );
9315+ res_digits [i ] = (NumericDigit ) (carry / divisor );
9316+ carry = carry % divisor ;
9317+ }
9318+ }
9319+ else
9320+ {
9321+ /* carry may exceed 64 bits */
9322+ uint128 carry = 0 ;
9323+
9324+ for (i = 0 ; i < res_ndigits ; i ++ )
9325+ {
9326+ carry = carry * NBASE + (i < var_ndigits ? var_digits [i ] : 0 );
9327+ res_digits [i ] = (NumericDigit ) (carry / divisor );
9328+ carry = carry % divisor ;
9329+ }
9330+ }
9331+
9332+ /* Store the quotient in result */
9333+ digitbuf_free (result -> buf );
9334+ result -> ndigits = res_ndigits ;
9335+ result -> buf = res_buf ;
9336+ result -> digits = res_digits ;
9337+ result -> weight = res_weight ;
9338+ result -> sign = res_sign ;
9339+
9340+ /* Round or truncate to target rscale (and set result->dscale) */
9341+ if (round )
9342+ round_var (result , rscale );
9343+ else
9344+ trunc_var (result , rscale );
9345+
9346+ /* Strip leading/trailing zeroes */
9347+ strip_var (result );
9348+ }
9349+ #endif
9350+
9351+
91859352/*
91869353 * Default scale selection for division
91879354 *
0 commit comments