bigdecimal 3.2.3 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,12 +29,16 @@
29
29
  #endif
30
30
 
31
31
  #include "bits.h"
32
+ #include "ntt.h"
33
+ #include "div.h"
32
34
  #include "static_assert.h"
33
35
 
34
- #define BIGDECIMAL_VERSION "3.2.3"
35
-
36
- /* #define ENABLE_NUMERIC_STRING */
36
+ #define BIGDECIMAL_VERSION "4.1.1"
37
37
 
38
+ /* Make sure VPMULT_BATCH_SIZE*BASE*BASE does not overflow DECDIG_DBL */
39
+ #define VPMULT_BATCH_SIZE 16
40
+ #define NTT_MULTIPLICATION_THRESHOLD 450
41
+ #define NEWTON_RAPHSON_DIVISION_THRESHOLD 100
38
42
  #define SIGNED_VALUE_MAX INTPTR_MAX
39
43
  #define SIGNED_VALUE_MIN INTPTR_MIN
40
44
  #define MUL_OVERFLOW_SIGNED_VALUE_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, SIGNED_VALUE_MIN, SIGNED_VALUE_MAX)
@@ -75,16 +79,6 @@ static struct {
75
79
  uint8_t mode;
76
80
  } rbd_rounding_modes[RBD_NUM_ROUNDING_MODES];
77
81
 
78
- typedef struct {
79
- VALUE bigdecimal;
80
- Real *real;
81
- } BDVALUE;
82
-
83
- typedef struct {
84
- VALUE bigdecimal_or_nil;
85
- Real *real_or_null;
86
- } NULLABLE_BDVALUE;
87
-
88
82
  static inline BDVALUE
89
83
  bdvalue_nonnullable(NULLABLE_BDVALUE v)
90
84
  {
@@ -160,42 +154,6 @@ rbd_struct_size(size_t const internal_digits)
160
154
  return offsetof(Real, frac) + frac_len * sizeof(DECDIG);
161
155
  }
162
156
 
163
- static inline Real *
164
- rbd_allocate_struct(size_t const internal_digits)
165
- {
166
- size_t const size = rbd_struct_size(internal_digits);
167
- Real *real = ruby_xcalloc(1, size);
168
- atomic_allocation_count_inc();
169
- real->MaxPrec = internal_digits;
170
- return real;
171
- }
172
-
173
- static inline Real *
174
- rbd_allocate_struct_decimal_digits(size_t const decimal_digits)
175
- {
176
- return rbd_allocate_struct(roomof(decimal_digits, BASE_FIG));
177
- }
178
-
179
- static void
180
- rbd_free_struct(Real *real)
181
- {
182
- if (real != NULL) {
183
- check_allocation_count_nonzero();
184
- ruby_xfree(real);
185
- atomic_allocation_count_dec_nounderflow();
186
- }
187
- }
188
-
189
- MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero(int sign, size_t const digits));
190
- #define NewZero rbd_allocate_struct_zero
191
- static inline Real *
192
- rbd_allocate_struct_zero(int sign, size_t const digits)
193
- {
194
- Real *real = rbd_allocate_struct_decimal_digits(digits);
195
- VpSetZero(real, sign);
196
- return real;
197
- }
198
-
199
157
  /*
200
158
  * ================== Ruby Interface part ==========================
201
159
  */
@@ -207,11 +165,9 @@ rbd_allocate_struct_zero(int sign, size_t const digits)
207
165
  static unsigned short VpGetException(void);
208
166
  static void VpSetException(unsigned short f);
209
167
  static void VpCheckException(Real *p, bool always);
210
- static int AddExponent(Real *a, SIGNED_VALUE n);
211
168
  static VALUE CheckGetValue(BDVALUE v);
212
169
  static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v);
213
170
  static int VpLimitRound(Real *c, size_t ixDigit);
214
- static Real *VpCopy(Real *pv, Real const* const x);
215
171
  static int VPrint(FILE *fp,const char *cntl_chr,Real *a);
216
172
 
217
173
  /*
@@ -226,38 +182,67 @@ static VALUE BigDecimal_negative_zero(void);
226
182
  static VALUE BigDecimal_addsub_with_coerce(VALUE self, VALUE r, size_t prec, int operation);
227
183
  static VALUE BigDecimal_mult_with_coerce(VALUE self, VALUE r, size_t prec);
228
184
 
229
- static void
230
- BigDecimal_delete(void *pv)
231
- {
232
- rbd_free_struct(pv);
233
- }
185
+ #ifndef HAVE_RB_EXT_RACTOR_SAFE
186
+ # undef RUBY_TYPED_FROZEN_SHAREABLE
187
+ # define RUBY_TYPED_FROZEN_SHAREABLE 0
188
+ #endif
189
+
190
+ #ifdef RUBY_TYPED_EMBEDDABLE
191
+ # define HAVE_RUBY_TYPED_EMBEDDABLE 1
192
+ #else
193
+ # ifdef HAVE_CONST_RUBY_TYPED_EMBEDDABLE
194
+ # define RUBY_TYPED_EMBEDDABLE RUBY_TYPED_EMBEDDABLE
195
+ # define HAVE_RUBY_TYPED_EMBEDDABLE 1
196
+ # else
197
+ # define RUBY_TYPED_EMBEDDABLE 0
198
+ # endif
199
+ #endif
234
200
 
235
201
  static size_t
236
202
  BigDecimal_memsize(const void *ptr)
237
203
  {
204
+ #ifdef HAVE_RUBY_TYPED_EMBEDDABLE
205
+ return 0; // Entirely embedded
206
+ #else
238
207
  const Real *pv = ptr;
239
208
  return (sizeof(*pv) + pv->MaxPrec * sizeof(DECDIG));
240
- }
241
-
242
- #ifndef HAVE_RB_EXT_RACTOR_SAFE
243
- # undef RUBY_TYPED_FROZEN_SHAREABLE
244
- # define RUBY_TYPED_FROZEN_SHAREABLE 0
245
209
  #endif
210
+ }
246
211
 
247
212
  static const rb_data_type_t BigDecimal_data_type = {
248
- "BigDecimal",
249
- { 0, BigDecimal_delete, BigDecimal_memsize, },
250
- #ifdef RUBY_TYPED_FREE_IMMEDIATELY
251
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED
252
- #endif
213
+ .wrap_struct_name = "BigDecimal",
214
+ .function = {
215
+ .dmark = 0,
216
+ .dfree = RUBY_DEFAULT_FREE,
217
+ .dsize = BigDecimal_memsize,
218
+ },
219
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
253
220
  };
254
221
 
255
222
  static VALUE
256
- BigDecimal_wrap_struct(VALUE klass, Real *real)
223
+ BigDecimal_allocate(size_t const internal_digits)
257
224
  {
258
- VALUE obj = TypedData_Wrap_Struct(klass, &BigDecimal_data_type, real);
259
- RB_OBJ_FREEZE(obj);
260
- return obj;
225
+ const size_t size = rbd_struct_size(internal_digits);
226
+ VALUE bd = rb_data_typed_object_zalloc(rb_cBigDecimal, size, &BigDecimal_data_type);
227
+ Real *vp;
228
+ TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
229
+ vp->MaxPrec = internal_digits;
230
+ RB_OBJ_FREEZE(bd);
231
+ return bd;
232
+ }
233
+
234
+ static VALUE
235
+ BigDecimal_allocate_decimal_digits(size_t const decimal_digits)
236
+ {
237
+ return BigDecimal_allocate(roomof(decimal_digits, BASE_FIG));
238
+ }
239
+
240
+ static Real *
241
+ VpPtr(VALUE obj)
242
+ {
243
+ Real *vp;
244
+ TypedData_Get_Struct(obj, Real, &BigDecimal_data_type, vp);
245
+ return vp;
261
246
  }
262
247
 
263
248
  MAYBE_UNUSED(static inline BDVALUE rbd_allocate_struct_zero_wrap(int sign, size_t const digits));
@@ -265,8 +250,10 @@ MAYBE_UNUSED(static inline BDVALUE rbd_allocate_struct_zero_wrap(int sign, size_
265
250
  static BDVALUE
266
251
  rbd_allocate_struct_zero_wrap(int sign, size_t const digits)
267
252
  {
268
- Real *real = rbd_allocate_struct_zero(sign, digits);
269
- return (BDVALUE) { BigDecimal_wrap_struct(rb_cBigDecimal, real), real };
253
+ VALUE obj = BigDecimal_allocate_decimal_digits(digits);
254
+ Real *real = VpPtr(obj);
255
+ VpSetZero(real, sign);
256
+ return (BDVALUE) { obj, real };
270
257
  }
271
258
 
272
259
  static inline int
@@ -324,20 +311,11 @@ GetBDValueWithPrecInternal(VALUE v, size_t prec, int must)
324
311
  break;
325
312
  }
326
313
 
327
- #ifdef ENABLE_NUMERIC_STRING
328
- case T_STRING: {
329
- const char *c_str = StringValueCStr(v);
330
- v = rb_cstr_convert_to_BigDecimal(c_str, must);
331
- break;
332
- }
333
- #endif /* ENABLE_NUMERIC_STRING */
334
-
335
314
  default:
336
315
  goto SomeOneMayDoIt;
337
316
  }
338
317
 
339
- Real *vp;
340
- TypedData_Get_Struct(v, Real, &BigDecimal_data_type, vp);
318
+ Real *vp = VpPtr(v);
341
319
  return (NULLABLE_BDVALUE) { v, vp };
342
320
 
343
321
  SomeOneMayDoIt:
@@ -388,37 +366,6 @@ BigDecimal_double_fig(VALUE self)
388
366
  return INT2FIX(BIGDECIMAL_DOUBLE_FIGURES);
389
367
  }
390
368
 
391
- /* call-seq:
392
- * precs -> array
393
- *
394
- * Returns an Array of two Integer values that represent platform-dependent
395
- * internal storage properties.
396
- *
397
- * This method is deprecated and will be removed in the future.
398
- * Instead, use BigDecimal#n_significant_digits for obtaining the number of
399
- * significant digits in scientific notation, and BigDecimal#precision for
400
- * obtaining the number of digits in decimal notation.
401
- *
402
- */
403
-
404
- static VALUE
405
- BigDecimal_prec(VALUE self)
406
- {
407
- BDVALUE v;
408
- VALUE obj;
409
-
410
- rb_category_warn(RB_WARN_CATEGORY_DEPRECATED,
411
- "BigDecimal#precs is deprecated and will be removed in the future; "
412
- "use BigDecimal#precision instead.");
413
-
414
- v = GetBDValueMust(self);
415
- obj = rb_assoc_new(SIZET2NUM(v.real->Prec*VpBaseFig()),
416
- SIZET2NUM(v.real->MaxPrec*VpBaseFig()));
417
-
418
- RB_GC_GUARD(v.bigdecimal);
419
- return obj;
420
- }
421
-
422
369
  static void
423
370
  VpCountPrecisionAndScale(Real *p, ssize_t *out_precision, ssize_t *out_scale)
424
371
  {
@@ -1015,7 +962,7 @@ BigDecimal_mode(int argc, VALUE *argv, VALUE self)
1015
962
  static size_t
1016
963
  GetAddSubPrec(Real *a, Real *b)
1017
964
  {
1018
- if (!VpIsDef(a) || !VpIsDef(b)) return (size_t)-1L;
965
+ if (VpIsZero(a) || VpIsZero(b)) return Max(a->Prec, b->Prec);
1019
966
  ssize_t min_a = a->exponent - a->Prec;
1020
967
  ssize_t min_b = b->exponent - b->Prec;
1021
968
  return Max(a->exponent, b->exponent) - Min(min_a, min_b);
@@ -1041,25 +988,18 @@ check_int_precision(VALUE v)
1041
988
  static NULLABLE_BDVALUE
1042
989
  CreateFromString(const char *str, VALUE klass, bool strict_p, bool raise_exception)
1043
990
  {
1044
- Real *pv = VpAlloc(str, strict_p, raise_exception);
1045
- if (!pv) return (NULLABLE_BDVALUE) { Qnil, NULL };
1046
- return (NULLABLE_BDVALUE) { BigDecimal_wrap_struct(klass, pv), pv };
991
+ return VpAlloc(str, strict_p, raise_exception);
1047
992
  }
1048
993
 
1049
- static Real *
1050
- VpCopy(Real *pv, Real const* const x)
994
+ void
995
+ VpMemCopy(Real *pv, Real const* const x)
1051
996
  {
1052
- assert(x != NULL);
1053
-
1054
- pv = (Real *)ruby_xrealloc(pv, rbd_struct_size(x->MaxPrec));
1055
997
  pv->MaxPrec = x->MaxPrec;
1056
998
  pv->Prec = x->Prec;
1057
999
  pv->exponent = x->exponent;
1058
1000
  pv->sign = x->sign;
1059
1001
  pv->flag = x->flag;
1060
1002
  MEMCPY(pv->frac, x->frac, DECDIG, pv->MaxPrec);
1061
-
1062
- return pv;
1063
1003
  }
1064
1004
 
1065
1005
  /* Returns True if the value is Not a Number. */
@@ -1099,9 +1039,6 @@ BigDecimal_check_num(Real *p)
1099
1039
  VpCheckException(p, true);
1100
1040
  }
1101
1041
 
1102
- static VALUE BigDecimal_fix(VALUE self);
1103
- static VALUE BigDecimal_split(VALUE self);
1104
-
1105
1042
  /* Returns the value as an Integer.
1106
1043
  *
1107
1044
  * If the BigDecimal is infinity or NaN, raises FloatDomainError.
@@ -1252,7 +1189,7 @@ GetCoercePrec(Real *a, size_t prec)
1252
1189
  static VALUE
1253
1190
  BigDecimal_coerce(VALUE self, VALUE other)
1254
1191
  {
1255
- Real* pv = DATA_PTR(self);
1192
+ Real* pv = VpPtr(self);
1256
1193
  BDVALUE b = GetBDValueWithPrecMust(other, GetCoercePrec(pv, 0));
1257
1194
  return rb_assoc_new(CheckGetValue(b), self);
1258
1195
  }
@@ -1318,13 +1255,32 @@ BigDecimal_addsub_with_coerce(VALUE self, VALUE r, size_t prec, int operation)
1318
1255
  if (VpIsNaN(a.real)) return CheckGetValue(a);
1319
1256
  if (VpIsNaN(b.real)) return CheckGetValue(b);
1320
1257
 
1321
- mx = GetAddSubPrec(a.real, b.real);
1322
- if (mx == (size_t)-1L) {
1323
- /* a or b is inf */
1258
+ if (VpIsInf(a.real) || VpIsInf(b.real)) {
1324
1259
  c = NewZeroWrap(1, BASE_FIG);
1325
1260
  VpAddSub(c.real, a.real, b.real, operation);
1326
1261
  }
1327
1262
  else {
1263
+
1264
+ // Optimization when exponent difference is large
1265
+ // (1.234e+1000).add(5.678e-1000, 10) == (1.234e+1000).add(0.1e+990, 10) in every rounding mode
1266
+ if (prec && !VpIsZero(a.real) && !VpIsZero(b.real)) {
1267
+ size_t precRoom = roomof(prec, BASE_FIG);
1268
+ if (a.real->exponent - (ssize_t)Max(a.real->Prec, precRoom) - 1 > b.real->exponent) {
1269
+ BDVALUE b2 = NewZeroWrap(1, BASE_FIG);
1270
+ VpSetOne(b2.real)
1271
+ VpSetSign(b2.real, b.real->sign);
1272
+ b2.real->exponent = a.real->exponent - (ssize_t)Max(a.real->Prec, precRoom) - 1;
1273
+ b = b2;
1274
+ } else if (b.real->exponent - (ssize_t)Max(b.real->Prec, precRoom) - 1 > a.real->exponent) {
1275
+ BDVALUE a2 = NewZeroWrap(1, BASE_FIG);
1276
+ VpSetOne(a2.real)
1277
+ VpSetSign(a2.real, a.real->sign);
1278
+ a2.real->exponent = b.real->exponent - (ssize_t)Max(b.real->Prec, precRoom) - 1;
1279
+ a = a2;
1280
+ }
1281
+ }
1282
+
1283
+ mx = GetAddSubPrec(a.real, b.real);
1328
1284
  c = NewZeroWrap(1, (mx + 1) * BASE_FIG);
1329
1285
  size_t pl = VpGetPrecLimit();
1330
1286
  if (prec) VpSetPrecLimit(prec);
@@ -1701,7 +1657,7 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE
1701
1657
 
1702
1658
  if (VpIsNaN(a.real) || VpIsNaN(b.real) || (VpIsInf(a.real) && VpIsInf(b.real))) {
1703
1659
  VALUE nan = BigDecimal_nan();
1704
- *div = *mod = (NULLABLE_BDVALUE) { nan, DATA_PTR(nan) };
1660
+ *div = *mod = (NULLABLE_BDVALUE) { nan, VpPtr(nan) };
1705
1661
  goto Done;
1706
1662
  }
1707
1663
  if (VpIsZero(b.real)) {
@@ -1710,25 +1666,35 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE
1710
1666
  if (VpIsInf(a.real)) {
1711
1667
  if (VpGetSign(a.real) == VpGetSign(b.real)) {
1712
1668
  VALUE inf = BigDecimal_positive_infinity();
1713
- *div = (NULLABLE_BDVALUE) { inf, DATA_PTR(inf) };
1669
+ *div = (NULLABLE_BDVALUE) { inf, VpPtr(inf) };
1714
1670
  }
1715
1671
  else {
1716
1672
  VALUE inf = BigDecimal_negative_infinity();
1717
- *div = (NULLABLE_BDVALUE) { inf, DATA_PTR(inf) };
1673
+ *div = (NULLABLE_BDVALUE) { inf, VpPtr(inf) };
1718
1674
  }
1719
1675
  VALUE nan = BigDecimal_nan();
1720
- *mod = (NULLABLE_BDVALUE) { nan, DATA_PTR(nan) };
1676
+ *mod = (NULLABLE_BDVALUE) { nan, VpPtr(nan) };
1721
1677
  goto Done;
1722
1678
  }
1723
- if (VpIsInf(b.real)) {
1679
+ if (VpIsZero(a.real)) {
1724
1680
  VALUE zero = BigDecimal_positive_zero();
1725
- *div = (NULLABLE_BDVALUE) { zero, DATA_PTR(zero) };
1681
+ *div = (NULLABLE_BDVALUE) { zero, VpPtr(zero) };
1726
1682
  *mod = bdvalue_nullable(a);
1727
1683
  goto Done;
1728
1684
  }
1729
- if (VpIsZero(a.real)) {
1730
- VALUE zero = BigDecimal_positive_zero();
1731
- *div = *mod = (NULLABLE_BDVALUE) { zero, DATA_PTR(zero) };
1685
+ if (VpIsInf(b.real)) {
1686
+ if (!truncate && VpGetSign(a.real) * VpGetSign(b.real) < 0) {
1687
+ BDVALUE minus_one = NewZeroWrap(1, BASE_FIG);
1688
+ VpSetOne(minus_one.real);
1689
+ VpSetSign(minus_one.real, -1);
1690
+ RB_GC_GUARD(minus_one.bigdecimal);
1691
+ *div = bdvalue_nullable(minus_one);
1692
+ *mod = bdvalue_nullable(b);
1693
+ } else {
1694
+ VALUE zero = BigDecimal_positive_zero();
1695
+ *div = (NULLABLE_BDVALUE) { zero, VpPtr(zero) };
1696
+ *mod = bdvalue_nullable(a);
1697
+ }
1732
1698
  goto Done;
1733
1699
  }
1734
1700
 
@@ -1844,7 +1810,7 @@ BigDecimal_divmod(VALUE self, VALUE r)
1844
1810
  NULLABLE_BDVALUE div, mod;
1845
1811
 
1846
1812
  if (BigDecimal_DoDivmod(self, r, &div, &mod, false)) {
1847
- return rb_assoc_new(CheckGetValue(bdvalue_nonnullable(div)), CheckGetValue(bdvalue_nonnullable(mod)));
1813
+ return rb_assoc_new(BigDecimal_to_i(CheckGetValue(bdvalue_nonnullable(div))), CheckGetValue(bdvalue_nonnullable(mod)));
1848
1814
  }
1849
1815
  return DoSomeOne(self,r,rb_intern("divmod"));
1850
1816
  }
@@ -2484,7 +2450,7 @@ BigDecimal_decimal_shift(VALUE self, VALUE v)
2484
2450
  prec = a.real->Prec + shiftDown;
2485
2451
  c = NewZeroWrap(1, prec * BASE_FIG);
2486
2452
  if (shift == 0) {
2487
- VpAsgn(c.real, a.real, 1);
2453
+ VpAsgn(c.real, a.real, 10);
2488
2454
  } else if (shiftDown) {
2489
2455
  DECDIG carry = 0;
2490
2456
  exponentShift++;
@@ -2570,9 +2536,7 @@ check_exception(VALUE bd)
2570
2536
  {
2571
2537
  assert(is_kind_of_BigDecimal(bd));
2572
2538
 
2573
- Real *vp;
2574
- TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
2575
- VpCheckException(vp, false);
2539
+ VpCheckException(VpPtr(bd), false);
2576
2540
 
2577
2541
  return bd;
2578
2542
  }
@@ -2580,16 +2544,19 @@ check_exception(VALUE bd)
2580
2544
  static VALUE
2581
2545
  rb_uint64_convert_to_BigDecimal(uint64_t uval)
2582
2546
  {
2547
+ VALUE bd;
2583
2548
  Real *vp;
2584
2549
  if (uval == 0) {
2585
- vp = rbd_allocate_struct(1);
2550
+ bd = BigDecimal_allocate(1);
2551
+ vp = VpPtr(bd);
2586
2552
  vp->Prec = 1;
2587
2553
  vp->exponent = 1;
2588
2554
  VpSetZero(vp, 1);
2589
2555
  vp->frac[0] = 0;
2590
2556
  }
2591
2557
  else if (uval < BASE) {
2592
- vp = rbd_allocate_struct(1);
2558
+ bd = BigDecimal_allocate(1);
2559
+ vp = VpPtr(bd);
2593
2560
  vp->Prec = 1;
2594
2561
  vp->exponent = 1;
2595
2562
  VpSetSign(vp, 1);
@@ -2614,14 +2581,15 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval)
2614
2581
  }
2615
2582
 
2616
2583
  const size_t exp = len + ntz;
2617
- vp = rbd_allocate_struct(len);
2584
+ bd = BigDecimal_allocate(len);
2585
+ vp = VpPtr(bd);
2618
2586
  vp->Prec = len;
2619
2587
  vp->exponent = exp;
2620
2588
  VpSetSign(vp, 1);
2621
2589
  MEMCPY(vp->frac, buf + BIGDECIMAL_INT64_MAX_LENGTH - len, DECDIG, len);
2622
2590
  }
2623
2591
 
2624
- return BigDecimal_wrap_struct(rb_cBigDecimal, vp);
2592
+ return bd;
2625
2593
  }
2626
2594
 
2627
2595
  static VALUE
@@ -2630,8 +2598,7 @@ rb_int64_convert_to_BigDecimal(int64_t ival)
2630
2598
  const uint64_t uval = (ival < 0) ? (((uint64_t)-(ival+1))+1) : (uint64_t)ival;
2631
2599
  VALUE bd = rb_uint64_convert_to_BigDecimal(uval);
2632
2600
  if (ival < 0) {
2633
- Real *vp;
2634
- TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
2601
+ Real *vp = VpPtr(bd);
2635
2602
  VpSetSign(vp, -1);
2636
2603
  }
2637
2604
  return bd;
@@ -2838,8 +2805,7 @@ rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
2838
2805
  }
2839
2806
 
2840
2807
  VALUE bd = rb_inum_convert_to_BigDecimal(inum);
2841
- Real *vp;
2842
- TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
2808
+ Real *vp = VpPtr(bd);
2843
2809
  assert(vp->Prec == prec);
2844
2810
  vp->exponent = exp;
2845
2811
 
@@ -2905,12 +2871,15 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
2905
2871
  if (digs == SIZE_MAX)
2906
2872
  return check_exception(val);
2907
2873
 
2908
- Real *vp;
2909
- TypedData_Get_Struct(val, Real, &BigDecimal_data_type, vp);
2910
- vp = VpCopy(NULL, vp);
2874
+ Real *vp = VpPtr(val);
2875
+
2876
+ VALUE copy = BigDecimal_allocate(vp->MaxPrec);
2877
+ Real *vp_copy = VpPtr(copy);
2878
+
2879
+ VpMemCopy(vp_copy, vp);
2880
+
2911
2881
  RB_GC_GUARD(val);
2912
2882
 
2913
- VALUE copy = BigDecimal_wrap_struct(rb_cBigDecimal, vp);
2914
2883
  /* TODO: rounding */
2915
2884
  return check_exception(copy);
2916
2885
  }
@@ -3232,19 +3201,39 @@ BigDecimal_literal(const char *str)
3232
3201
 
3233
3202
  #ifdef BIGDECIMAL_USE_VP_TEST_METHODS
3234
3203
  VALUE
3235
- BigDecimal_vpdivd(VALUE self, VALUE r, VALUE cprec) {
3236
- BDVALUE a,b,c,d;
3204
+ BigDecimal_vpdivd_generic(VALUE self, VALUE r, VALUE cprec, void (*vpdivd_func)(Real*, Real*, Real*, Real*)) {
3205
+ BDVALUE a, b, c, d;
3237
3206
  size_t cn = NUM2INT(cprec);
3238
3207
  a = GetBDValueMust(self);
3239
3208
  b = GetBDValueMust(r);
3240
3209
  c = NewZeroWrap(1, cn * BASE_FIG);
3241
3210
  d = NewZeroWrap(1, VPDIVD_REM_PREC(a.real, b.real, c.real) * BASE_FIG);
3242
- VpDivd(c.real, d.real, a.real, b.real);
3211
+ vpdivd_func(c.real, d.real, a.real, b.real);
3243
3212
  RB_GC_GUARD(a.bigdecimal);
3244
3213
  RB_GC_GUARD(b.bigdecimal);
3245
3214
  return rb_assoc_new(c.bigdecimal, d.bigdecimal);
3246
3215
  }
3247
3216
 
3217
+ void
3218
+ VpDivdNormal(Real *c, Real *r, Real *a, Real *b) {
3219
+ VpDivd(c, r, a, b);
3220
+ }
3221
+
3222
+ VALUE
3223
+ BigDecimal_vpdivd(VALUE self, VALUE r, VALUE cprec) {
3224
+ return BigDecimal_vpdivd_generic(self, r, cprec, VpDivdNormal);
3225
+ }
3226
+
3227
+ VALUE
3228
+ BigDecimal_vpdivd_newton(VALUE self, VALUE r, VALUE cprec) {
3229
+ return BigDecimal_vpdivd_generic(self, r, cprec, VpDivdNewton);
3230
+ }
3231
+
3232
+ VALUE
3233
+ BigDecimal_newton_raphson_inverse(VALUE self, VALUE prec) {
3234
+ return newton_raphson_inverse(self, NUM2SIZET(prec));
3235
+ }
3236
+
3248
3237
  VALUE
3249
3238
  BigDecimal_vpmult(VALUE self, VALUE v) {
3250
3239
  BDVALUE a,b,c;
@@ -3256,6 +3245,23 @@ BigDecimal_vpmult(VALUE self, VALUE v) {
3256
3245
  RB_GC_GUARD(b.bigdecimal);
3257
3246
  return c.bigdecimal;
3258
3247
  }
3248
+
3249
+ VALUE
3250
+ BigDecimal_nttmult(VALUE self, VALUE v) {
3251
+ BDVALUE a,b,c;
3252
+ a = GetBDValueMust(self);
3253
+ b = GetBDValueMust(v);
3254
+ c = NewZeroWrap(1, VPMULT_RESULT_PREC(a.real, b.real) * BASE_FIG);
3255
+ ntt_multiply(a.real->Prec, b.real->Prec, a.real->frac, b.real->frac, c.real->frac);
3256
+ VpSetSign(c.real, a.real->sign * b.real->sign);
3257
+ c.real->exponent = a.real->exponent + b.real->exponent;
3258
+ c.real->Prec = a.real->Prec + b.real->Prec;
3259
+ VpNmlz(c.real);
3260
+ RB_GC_GUARD(a.bigdecimal);
3261
+ RB_GC_GUARD(b.bigdecimal);
3262
+ return c.bigdecimal;
3263
+ }
3264
+
3259
3265
  #endif /* BIGDECIMAL_USE_VP_TEST_METHODS */
3260
3266
 
3261
3267
  /* Document-class: BigDecimal
@@ -3568,7 +3574,6 @@ Init_bigdecimal(void)
3568
3574
  rb_define_const(rb_cBigDecimal, "NAN", BIGDECIMAL_LITERAL(NAN, NaN));
3569
3575
 
3570
3576
  /* instance methods */
3571
- rb_define_method(rb_cBigDecimal, "precs", BigDecimal_prec, 0);
3572
3577
  rb_define_method(rb_cBigDecimal, "precision", BigDecimal_precision, 0);
3573
3578
  rb_define_method(rb_cBigDecimal, "scale", BigDecimal_scale, 0);
3574
3579
  rb_define_method(rb_cBigDecimal, "precision_scale", BigDecimal_precision_scale, 0);
@@ -3627,7 +3632,10 @@ Init_bigdecimal(void)
3627
3632
 
3628
3633
  #ifdef BIGDECIMAL_USE_VP_TEST_METHODS
3629
3634
  rb_define_method(rb_cBigDecimal, "vpdivd", BigDecimal_vpdivd, 2);
3635
+ rb_define_method(rb_cBigDecimal, "vpdivd_newton", BigDecimal_vpdivd_newton, 2);
3636
+ rb_define_method(rb_cBigDecimal, "newton_raphson_inverse", BigDecimal_newton_raphson_inverse, 1);
3630
3637
  rb_define_method(rb_cBigDecimal, "vpmult", BigDecimal_vpmult, 1);
3638
+ rb_define_method(rb_cBigDecimal, "nttmult", BigDecimal_nttmult, 1);
3631
3639
  #endif /* BIGDECIMAL_USE_VP_TEST_METHODS */
3632
3640
 
3633
3641
  #define ROUNDING_MODE(i, name, value) \
@@ -3670,7 +3678,7 @@ Init_bigdecimal(void)
3670
3678
  static int gfDebug = 1; /* Debug switch */
3671
3679
  #endif /* BIGDECIMAL_DEBUG */
3672
3680
 
3673
- static Real *VpConstOne; /* constant 1.0 */
3681
+ static VALUE VpConstOne; /* constant 1.0 */
3674
3682
 
3675
3683
  enum op_sw {
3676
3684
  OP_SW_ADD = 1, /* + */
@@ -4071,8 +4079,9 @@ VpInit(DECDIG BaseVal)
4071
4079
  VpGetDoubleNegZero();
4072
4080
 
4073
4081
  /* Const 1.0 */
4074
- VpConstOne = NewZero(1, 1);
4075
- VpSetOne(VpConstOne);
4082
+ rb_global_variable(&VpConstOne);
4083
+ VpConstOne = NewZeroWrap(1, 1).bigdecimal;
4084
+ VpSetOne(VpPtr(VpConstOne));
4076
4085
 
4077
4086
  #ifdef BIGDECIMAL_DEBUG
4078
4087
  gnAlloc = 0;
@@ -4084,7 +4093,7 @@ VpInit(DECDIG BaseVal)
4084
4093
  VP_EXPORT Real *
4085
4094
  VpOne(void)
4086
4095
  {
4087
- return VpConstOne;
4096
+ return VpPtr(VpConstOne);
4088
4097
  }
4089
4098
 
4090
4099
  /* If exponent overflows,then raise exception or returns 0 */
@@ -4115,7 +4124,7 @@ overflow:
4115
4124
  return VpException(VP_EXCEPTION_OVERFLOW, "Exponent overflow", 0);
4116
4125
  }
4117
4126
 
4118
- Real *
4127
+ NULLABLE_BDVALUE
4119
4128
  bigdecimal_parse_special_string(const char *str)
4120
4129
  {
4121
4130
  static const struct {
@@ -4140,66 +4149,27 @@ bigdecimal_parse_special_string(const char *str)
4140
4149
  p = str + table[i].len;
4141
4150
  while (*p && ISSPACE(*p)) ++p;
4142
4151
  if (*p == '\0') {
4143
- Real *vp = rbd_allocate_struct(1);
4152
+ VALUE obj = BigDecimal_allocate(1);
4153
+ Real *vp = VpPtr(obj);
4144
4154
  switch (table[i].sign) {
4145
4155
  default:
4146
- UNREACHABLE; break;
4156
+ UNREACHABLE;
4157
+ return (NULLABLE_BDVALUE) { Qnil, NULL };
4147
4158
  case VP_SIGN_POSITIVE_INFINITE:
4148
4159
  VpSetPosInf(vp);
4149
- return vp;
4160
+ break;
4150
4161
  case VP_SIGN_NEGATIVE_INFINITE:
4151
4162
  VpSetNegInf(vp);
4152
- return vp;
4163
+ break;
4153
4164
  case VP_SIGN_NaN:
4154
4165
  VpSetNaN(vp);
4155
- return vp;
4166
+ break;
4156
4167
  }
4168
+ return (NULLABLE_BDVALUE) { obj, vp };
4157
4169
  }
4158
4170
  }
4159
4171
 
4160
- return NULL;
4161
- }
4162
-
4163
- struct VpCtoV_args {
4164
- Real *a;
4165
- const char *int_chr;
4166
- size_t ni;
4167
- const char *frac;
4168
- size_t nf;
4169
- const char *exp_chr;
4170
- size_t ne;
4171
- };
4172
-
4173
- static VALUE
4174
- call_VpCtoV(VALUE arg)
4175
- {
4176
- struct VpCtoV_args *x = (struct VpCtoV_args *)arg;
4177
- return (VALUE)VpCtoV(x->a, x->int_chr, x->ni, x->frac, x->nf, x->exp_chr, x->ne);
4178
- }
4179
-
4180
- static int
4181
- protected_VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size_t nf, const char *exp_chr, size_t ne, int free_on_error)
4182
- {
4183
- struct VpCtoV_args args;
4184
- int state = 0;
4185
-
4186
- args.a = a;
4187
- args.int_chr = int_chr;
4188
- args.ni = ni;
4189
- args.frac = frac;
4190
- args.nf = nf;
4191
- args.exp_chr = exp_chr;
4192
- args.ne = ne;
4193
-
4194
- VALUE result = rb_protect(call_VpCtoV, (VALUE)&args, &state);
4195
- if (state) {
4196
- if (free_on_error) {
4197
- rbd_free_struct(a);
4198
- }
4199
- rb_jump_tag(state);
4200
- }
4201
-
4202
- return (int)result;
4172
+ return (NULLABLE_BDVALUE) { Qnil, NULL };
4203
4173
  }
4204
4174
 
4205
4175
  /*
@@ -4208,25 +4178,25 @@ protected_VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size
4208
4178
  * szVal ... The value assigned(char).
4209
4179
  *
4210
4180
  * [Returns]
4211
- * Pointer to the newly allocated variable, or
4212
- * NULL be returned if memory allocation is failed,or any error.
4181
+ * NULLABLE_BDVALUE to the newly allocated variable.
4182
+ * Null is returned if memory allocation failed, or any error occured.
4213
4183
  */
4214
- VP_EXPORT Real *
4184
+ VP_EXPORT NULLABLE_BDVALUE
4215
4185
  VpAlloc(const char *szVal, int strict_p, int exc)
4216
4186
  {
4217
4187
  const char *orig_szVal = szVal;
4218
4188
  size_t i, j, ni, ipf, nf, ipe, ne, exp_seen, nalloc;
4219
4189
  char v, *psz;
4220
4190
  int sign=1;
4221
- Real *vp = NULL;
4222
4191
  VALUE buf;
4223
4192
 
4224
4193
  /* Skipping leading spaces */
4225
4194
  while (ISSPACE(*szVal)) szVal++;
4226
4195
 
4227
4196
  /* Check on Inf & NaN */
4228
- if ((vp = bigdecimal_parse_special_string(szVal)) != NULL) {
4229
- return vp;
4197
+ NULLABLE_BDVALUE special_bd = bigdecimal_parse_special_string(szVal);
4198
+ if (special_bd.real_or_null != NULL) {
4199
+ return special_bd;
4230
4200
  }
4231
4201
 
4232
4202
  /* Skip leading `#`.
@@ -4380,10 +4350,11 @@ VpAlloc(const char *szVal, int strict_p, int exc)
4380
4350
  VALUE str;
4381
4351
  invalid_value:
4382
4352
  if (!strict_p) {
4383
- return NewZero(1, 1);
4353
+ BDVALUE res = rbd_allocate_struct_zero_wrap(1, 1);
4354
+ return (NULLABLE_BDVALUE) { res.bigdecimal, res.real };
4384
4355
  }
4385
4356
  if (!exc) {
4386
- return NULL;
4357
+ return (NULLABLE_BDVALUE) { Qnil, NULL };
4387
4358
  }
4388
4359
  str = rb_str_new2(orig_szVal);
4389
4360
  rb_raise(rb_eArgError, "invalid value for BigDecimal(): \"%"PRIsVALUE"\"", str);
@@ -4391,11 +4362,12 @@ VpAlloc(const char *szVal, int strict_p, int exc)
4391
4362
 
4392
4363
  nalloc = (ni + nf + BASE_FIG - 1) / BASE_FIG + 1; /* set effective allocation */
4393
4364
  /* units for szVal[] */
4394
- vp = rbd_allocate_struct(nalloc);
4365
+ VALUE obj = BigDecimal_allocate(nalloc);
4366
+ Real *vp = VpPtr(obj);
4395
4367
  VpSetZero(vp, sign);
4396
- protected_VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne, true);
4368
+ VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne);
4397
4369
  rb_str_resize(buf, 0);
4398
- return vp;
4370
+ return (NULLABLE_BDVALUE) { obj, vp };
4399
4371
  }
4400
4372
 
4401
4373
  /*
@@ -4867,17 +4839,12 @@ VpSetPTR(Real *a, Real *b, Real *c, size_t *a_pos, size_t *b_pos, size_t *c_pos,
4867
4839
  * a0 a1 .... an * b0
4868
4840
  * +_____________________________
4869
4841
  * c0 c1 c2 ...... cl
4870
- * nc <---|
4871
- * MaxAB |--------------------|
4872
4842
  */
4873
4843
  VP_EXPORT size_t
4874
4844
  VpMult(Real *c, Real *a, Real *b)
4875
4845
  {
4876
- size_t MxIndA, MxIndB, MxIndAB;
4877
- size_t ind_c, i, ii, nc;
4878
- size_t ind_as, ind_ae, ind_bs;
4879
- DECDIG carry;
4880
- DECDIG_DBL s;
4846
+ ssize_t a_batch_max, b_batch_max;
4847
+ DECDIG_DBL batch[VPMULT_BATCH_SIZE * 2 - 1];
4881
4848
 
4882
4849
  if (!VpIsDefOP(c, a, b, OP_SW_MULT)) return 0; /* No significant digit */
4883
4850
 
@@ -4901,61 +4868,57 @@ VpMult(Real *c, Real *a, Real *b)
4901
4868
  a = b;
4902
4869
  b = w;
4903
4870
  }
4904
- MxIndA = a->Prec - 1;
4905
- MxIndB = b->Prec - 1;
4906
- MxIndAB = a->Prec + b->Prec - 1;
4907
4871
 
4908
4872
  /* set LHSV c info */
4909
4873
 
4910
4874
  c->exponent = a->exponent; /* set exponent */
4911
4875
  VpSetSign(c, VpGetSign(a) * VpGetSign(b)); /* set sign */
4912
4876
  if (!AddExponent(c, b->exponent)) return 0;
4913
- carry = 0;
4914
- nc = ind_c = MxIndAB;
4915
- memset(c->frac, 0, (nc + 1) * sizeof(DECDIG)); /* Initialize c */
4916
- c->Prec = nc + 1; /* set precision */
4917
- for (nc = 0; nc < MxIndAB; ++nc, --ind_c) {
4918
- if (nc < MxIndB) { /* The left triangle of the Fig. */
4919
- ind_as = MxIndA - nc;
4920
- ind_ae = MxIndA;
4921
- ind_bs = MxIndB;
4922
- }
4923
- else if (nc <= MxIndA) { /* The middle rectangular of the Fig. */
4924
- ind_as = MxIndA - nc;
4925
- ind_ae = MxIndA - (nc - MxIndB);
4926
- ind_bs = MxIndB;
4927
- }
4928
- else /* if (nc > MxIndA) */ { /* The right triangle of the Fig. */
4929
- ind_as = 0;
4930
- ind_ae = MxIndAB - nc - 1;
4931
- ind_bs = MxIndB - (nc - MxIndA);
4932
- }
4933
4877
 
4934
- for (i = ind_as; i <= ind_ae; ++i) {
4935
- s = (DECDIG_DBL)a->frac[i] * b->frac[ind_bs--];
4936
- carry = (DECDIG)(s / BASE);
4937
- s -= (DECDIG_DBL)carry * BASE;
4938
- c->frac[ind_c] += (DECDIG)s;
4939
- if (c->frac[ind_c] >= BASE) {
4940
- s = c->frac[ind_c] / BASE;
4941
- carry += (DECDIG)s;
4942
- c->frac[ind_c] -= (DECDIG)(s * BASE);
4878
+ if (b->Prec >= NTT_MULTIPLICATION_THRESHOLD) {
4879
+ ntt_multiply(a->Prec, b->Prec, a->frac, b->frac, c->frac);
4880
+ c->Prec = a->Prec + b->Prec;
4881
+ goto Cleanup;
4882
+ }
4883
+
4884
+ c->Prec = a->Prec + b->Prec; /* set precision */
4885
+ memset(c->frac, 0, c->Prec * sizeof(DECDIG)); /* Initialize c */
4886
+
4887
+ // Process VPMULT_BATCH_SIZE decdigits at a time to reduce the number of carry operations.
4888
+ a_batch_max = (a->Prec - 1) / VPMULT_BATCH_SIZE;
4889
+ b_batch_max = (b->Prec - 1) / VPMULT_BATCH_SIZE;
4890
+ for (ssize_t ibatch = a_batch_max; ibatch >= 0; ibatch--) {
4891
+ int isize = ibatch == a_batch_max ? (a->Prec - 1) % VPMULT_BATCH_SIZE + 1 : VPMULT_BATCH_SIZE;
4892
+ for (ssize_t jbatch = b_batch_max; jbatch >= 0; jbatch--) {
4893
+ int jsize = jbatch == b_batch_max ? (b->Prec - 1) % VPMULT_BATCH_SIZE + 1 : VPMULT_BATCH_SIZE;
4894
+ memset(batch, 0, (isize + jsize - 1) * sizeof(DECDIG_DBL));
4895
+
4896
+ // Perform multiplication without carry calculation.
4897
+ // BASE * BASE * VPMULT_BATCH_SIZE < 2**64 should be satisfied so that
4898
+ // DECDIG_DBL can hold the intermediate sum without overflow.
4899
+ for (int i = 0; i < isize; i++) {
4900
+ for (int j = 0; j < jsize; j++) {
4901
+ batch[i + j] += (DECDIG_DBL)a->frac[ibatch * VPMULT_BATCH_SIZE + i] * b->frac[jbatch * VPMULT_BATCH_SIZE + j];
4902
+ }
4943
4903
  }
4944
- if (carry) {
4945
- ii = ind_c;
4946
- while (ii-- > 0) {
4947
- c->frac[ii] += carry;
4948
- if (c->frac[ii] >= BASE) {
4949
- carry = c->frac[ii] / BASE;
4950
- c->frac[ii] -= (carry * BASE);
4951
- }
4952
- else {
4953
- break;
4954
- }
4955
- }
4956
- }
4957
- }
4904
+
4905
+ // Add the batch result to c with carry calculation.
4906
+ DECDIG_DBL carry = 0;
4907
+ for (int k = isize + jsize - 2; k >= 0; k--) {
4908
+ size_t l = (ibatch + jbatch) * VPMULT_BATCH_SIZE + k + 1;
4909
+ DECDIG_DBL s = c->frac[l] + batch[k] + carry;
4910
+ c->frac[l] = (DECDIG)(s % BASE);
4911
+ carry = (DECDIG_DBL)(s / BASE);
4912
+ }
4913
+
4914
+ // Adding carry may exceed BASE, but it won't cause overflow of DECDIG.
4915
+ // Exceeded value will be resolved in the carry operation of next (ibatch + jbatch - 1) batch.
4916
+ // WARNING: This safety strongly relies on the current nested loop execution order.
4917
+ c->frac[(ibatch + jbatch) * VPMULT_BATCH_SIZE] += (DECDIG)carry;
4918
+ }
4958
4919
  }
4920
+
4921
+ Cleanup:
4959
4922
  VpNmlz(c);
4960
4923
 
4961
4924
  Exit:
@@ -5003,6 +4966,11 @@ VpDivd(Real *c, Real *r, Real *a, Real *b)
5003
4966
 
5004
4967
  if (word_a > word_r || word_b + word_c - 2 >= word_r) goto space_error;
5005
4968
 
4969
+ if (word_c >= NEWTON_RAPHSON_DIVISION_THRESHOLD && word_b >= NEWTON_RAPHSON_DIVISION_THRESHOLD) {
4970
+ VpDivdNewton(c, r, a, b);
4971
+ goto Exit;
4972
+ }
4973
+
5006
4974
  for (i = 0; i < word_a; ++i) r->frac[i] = a->frac[i];
5007
4975
  for (i = word_a; i < word_r; ++i) r->frac[i] = 0;
5008
4976
  for (i = 0; i < word_c; ++i) c->frac[i] = 0;
@@ -6141,7 +6109,7 @@ VpFrac(Real *y, Real *x)
6141
6109
  size_t my, ind_y, ind_x;
6142
6110
 
6143
6111
  if (!VpHasVal(x)) {
6144
- VpAsgn(y, x, 1);
6112
+ VpAsgn(y, x, 10);
6145
6113
  goto Exit;
6146
6114
  }
6147
6115
 
@@ -6150,7 +6118,7 @@ VpFrac(Real *y, Real *x)
6150
6118
  goto Exit;
6151
6119
  }
6152
6120
  else if (x->exponent <= 0) {
6153
- VpAsgn(y, x, 1);
6121
+ VpAsgn(y, x, 10);
6154
6122
  goto Exit;
6155
6123
  }
6156
6124