Skip to content

Commit 0268623

Browse files
committed
Multiplication with 8-decdig batch
Perform multiplication with 8-decdigs * 8-decdigs batch. This will reduce carry operation by 1/8, and may enable vectorization.
1 parent e64c502 commit 0268623

2 files changed

Lines changed: 50 additions & 55 deletions

File tree

ext/bigdecimal/bigdecimal.c

Lines changed: 36 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@
3535

3636
#define BIGDECIMAL_VERSION "4.1.0"
3737

38-
#define NTT_MULTIPLICATION_THRESHOLD 100
39-
#define NEWTON_RAPHSON_DIVISION_THRESHOLD 200
38+
#define NTT_MULTIPLICATION_THRESHOLD 200
39+
#define NEWTON_RAPHSON_DIVISION_THRESHOLD 100
4040
#define SIGNED_VALUE_MAX INTPTR_MAX
4141
#define SIGNED_VALUE_MIN INTPTR_MIN
4242
#define MUL_OVERFLOW_SIGNED_VALUE_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, SIGNED_VALUE_MIN, SIGNED_VALUE_MAX)
@@ -4904,17 +4904,12 @@ VpSetPTR(Real *a, Real *b, Real *c, size_t *a_pos, size_t *b_pos, size_t *c_pos,
49044904
* a0 a1 .... an * b0
49054905
* +_____________________________
49064906
* c0 c1 c2 ...... cl
4907-
* nc <---|
4908-
* MaxAB |--------------------|
49094907
*/
49104908
VP_EXPORT size_t
49114909
VpMult(Real *c, Real *a, Real *b)
49124910
{
4913-
size_t MxIndA, MxIndB, MxIndAB;
4914-
size_t ind_c, i, ii, nc;
4915-
size_t ind_as, ind_ae, ind_bs;
4916-
DECDIG carry;
4917-
DECDIG_DBL s;
4911+
ssize_t a_batch_max, b_batch_max;
4912+
DECDIG_DBL batch[15];
49184913

49194914
if (!VpIsDefOP(c, a, b, OP_SW_MULT)) return 0; /* No significant digit */
49204915

@@ -4938,9 +4933,6 @@ VpMult(Real *c, Real *a, Real *b)
49384933
a = b;
49394934
b = w;
49404935
}
4941-
MxIndA = a->Prec - 1;
4942-
MxIndB = b->Prec - 1;
4943-
MxIndAB = a->Prec + b->Prec - 1;
49444936

49454937
/* set LHSV c info */
49464938

@@ -4954,51 +4946,40 @@ VpMult(Real *c, Real *a, Real *b)
49544946
goto Cleanup;
49554947
}
49564948

4957-
carry = 0;
4958-
nc = ind_c = MxIndAB;
4959-
memset(c->frac, 0, (nc + 1) * sizeof(DECDIG)); /* Initialize c */
4960-
c->Prec = nc + 1; /* set precision */
4961-
for (nc = 0; nc < MxIndAB; ++nc, --ind_c) {
4962-
if (nc < MxIndB) { /* The left triangle of the Fig. */
4963-
ind_as = MxIndA - nc;
4964-
ind_ae = MxIndA;
4965-
ind_bs = MxIndB;
4966-
}
4967-
else if (nc <= MxIndA) { /* The middle rectangular of the Fig. */
4968-
ind_as = MxIndA - nc;
4969-
ind_ae = MxIndA - (nc - MxIndB);
4970-
ind_bs = MxIndB;
4971-
}
4972-
else /* if (nc > MxIndA) */ { /* The right triangle of the Fig. */
4973-
ind_as = 0;
4974-
ind_ae = MxIndAB - nc - 1;
4975-
ind_bs = MxIndB - (nc - MxIndA);
4976-
}
4949+
c->Prec = a->Prec + b->Prec; /* set precision */
4950+
memset(c->frac, 0, c->Prec * sizeof(DECDIG)); /* Initialize c */
49774951

4978-
for (i = ind_as; i <= ind_ae; ++i) {
4979-
s = (DECDIG_DBL)a->frac[i] * b->frac[ind_bs--];
4980-
carry = (DECDIG)(s / BASE);
4981-
s -= (DECDIG_DBL)carry * BASE;
4982-
c->frac[ind_c] += (DECDIG)s;
4983-
if (c->frac[ind_c] >= BASE) {
4984-
s = c->frac[ind_c] / BASE;
4985-
carry += (DECDIG)s;
4986-
c->frac[ind_c] -= (DECDIG)(s * BASE);
4952+
// Process 8 decdigits at a time to reduce the number of carry operations.
4953+
a_batch_max = (a->Prec - 1) / 8;
4954+
b_batch_max = (b->Prec - 1) / 8;
4955+
for (ssize_t ibatch = a_batch_max; ibatch >= 0; ibatch--) {
4956+
int isize = ibatch == a_batch_max ? (a->Prec - 1) % 8 + 1 : 8;
4957+
for (ssize_t jbatch = b_batch_max; jbatch >= 0; jbatch--) {
4958+
int jsize = jbatch == b_batch_max ? (b->Prec - 1) % 8 + 1 : 8;
4959+
memset(batch, 0, (isize + jsize - 1) * sizeof(DECDIG_DBL));
4960+
4961+
// Perform multiplication without carry calculation.
4962+
// 999999999 * 999999999 * 8 < 2**63 - 1, so DECDIG_DBL can hold the intermediate sum without overflow.
4963+
for (int i = 0; i < isize; i++) {
4964+
for (int j = 0; j < jsize; j++) {
4965+
batch[i + j] += (DECDIG_DBL)a->frac[ibatch * 8 + i] * b->frac[jbatch * 8 + j];
4966+
}
49874967
}
4988-
if (carry) {
4989-
ii = ind_c;
4990-
while (ii-- > 0) {
4991-
c->frac[ii] += carry;
4992-
if (c->frac[ii] >= BASE) {
4993-
carry = c->frac[ii] / BASE;
4994-
c->frac[ii] -= (carry * BASE);
4995-
}
4996-
else {
4997-
break;
4998-
}
4999-
}
5000-
}
5001-
}
4968+
4969+
// Add the batch result to c with carry calculation.
4970+
DECDIG_DBL carry = 0;
4971+
for (int k = isize + jsize - 2; k >= 0; k--) {
4972+
size_t l = (ibatch + jbatch) * 8 + k + 1;
4973+
DECDIG_DBL s = c->frac[l] + batch[k] + carry;
4974+
c->frac[l] = (DECDIG)(s % BASE);
4975+
carry = (DECDIG_DBL)(s / BASE);
4976+
}
4977+
4978+
// Adding carry may exceed BASE, but it won't cause overflow of DECDIG.
4979+
// Exceeded value will be normalized in the carry operation of next (ibatch + jbatch - 1) batch.
4980+
// WARNING: This safety strongly relies on the current nested loop execution order.
4981+
c->frac[(ibatch + jbatch) * 8] += (DECDIG)carry;
4982+
}
50024983
}
50034984

50044985
Cleanup:

test/bigdecimal/test_vp_operation.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ def setup
1414
end
1515

1616
def test_vpmult
17+
# Max carry case
18+
[*32...40].repeated_permutation(2) do |n, m|
19+
x = BigDecimal('9' * BASE_FIG * n)
20+
y = BigDecimal('9' * BASE_FIG * m)
21+
assert_equal(x.to_i * y.to_i, x.vpmult(y))
22+
end
23+
1724
assert_equal(BigDecimal('121932631112635269'), BigDecimal('123456789').vpmult(BigDecimal('987654321')))
1825
assert_equal(BigDecimal('12193263.1112635269'), BigDecimal('123.456789').vpmult(BigDecimal('98765.4321')))
1926
x = 123**456
@@ -22,6 +29,13 @@ def test_vpmult
2229
end
2330

2431
def test_nttmult
32+
# Max carry case
33+
[*32...40].repeated_permutation(2) do |n, m|
34+
x = BigDecimal('9' * BASE_FIG * n)
35+
y = BigDecimal('9' * BASE_FIG * m)
36+
assert_equal(x.to_i * y.to_i, x.nttmult(y))
37+
end
38+
2539
[*1..32].repeated_permutation(2) do |a, b|
2640
x = BigDecimal(10 ** (BASE_FIG * a) / 7)
2741
y = BigDecimal(10 ** (BASE_FIG * b) / 13)

0 commit comments

Comments
 (0)