bigdecimal 3.3.1 → 4.1.3

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.3.1"
35
-
36
- /* #define ENABLE_NUMERIC_STRING */
36
+ #define BIGDECIMAL_VERSION "4.1.3"
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)
@@ -64,7 +68,6 @@ static ID id_banker;
64
68
  static ID id_ceiling;
65
69
  static ID id_ceil;
66
70
  static ID id_floor;
67
- static ID id_to_r;
68
71
  static ID id_eq;
69
72
  static ID id_half;
70
73
 
@@ -75,16 +78,6 @@ static struct {
75
78
  uint8_t mode;
76
79
  } rbd_rounding_modes[RBD_NUM_ROUNDING_MODES];
77
80
 
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
81
  static inline BDVALUE
89
82
  bdvalue_nonnullable(NULLABLE_BDVALUE v)
90
83
  {
@@ -160,42 +153,6 @@ rbd_struct_size(size_t const internal_digits)
160
153
  return offsetof(Real, frac) + frac_len * sizeof(DECDIG);
161
154
  }
162
155
 
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
156
  /*
200
157
  * ================== Ruby Interface part ==========================
201
158
  */
@@ -207,11 +164,9 @@ rbd_allocate_struct_zero(int sign, size_t const digits)
207
164
  static unsigned short VpGetException(void);
208
165
  static void VpSetException(unsigned short f);
209
166
  static void VpCheckException(Real *p, bool always);
210
- static int AddExponent(Real *a, SIGNED_VALUE n);
211
167
  static VALUE CheckGetValue(BDVALUE v);
212
168
  static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v);
213
169
  static int VpLimitRound(Real *c, size_t ixDigit);
214
- static Real *VpCopy(Real *pv, Real const* const x);
215
170
  static int VPrint(FILE *fp,const char *cntl_chr,Real *a);
216
171
 
217
172
  /*
@@ -226,49 +181,71 @@ static VALUE BigDecimal_negative_zero(void);
226
181
  static VALUE BigDecimal_addsub_with_coerce(VALUE self, VALUE r, size_t prec, int operation);
227
182
  static VALUE BigDecimal_mult_with_coerce(VALUE self, VALUE r, size_t prec);
228
183
 
229
- static void
230
- BigDecimal_delete(void *pv)
231
- {
232
- rbd_free_struct(pv);
233
- }
184
+ #ifndef HAVE_RB_EXT_RACTOR_SAFE
185
+ # undef RUBY_TYPED_FROZEN_SHAREABLE
186
+ # define RUBY_TYPED_FROZEN_SHAREABLE 0
187
+ #endif
188
+
189
+ #ifdef RUBY_TYPED_EMBEDDABLE
190
+ # define HAVE_RUBY_TYPED_EMBEDDABLE 1
191
+ #else
192
+ # ifdef HAVE_CONST_RUBY_TYPED_EMBEDDABLE
193
+ # define RUBY_TYPED_EMBEDDABLE RUBY_TYPED_EMBEDDABLE
194
+ # define HAVE_RUBY_TYPED_EMBEDDABLE 1
195
+ # else
196
+ # define RUBY_TYPED_EMBEDDABLE 0
197
+ # endif
198
+ #endif
234
199
 
235
200
  static size_t
236
201
  BigDecimal_memsize(const void *ptr)
237
202
  {
203
+ #ifdef HAVE_RUBY_TYPED_EMBEDDABLE
204
+ return 0; // Entirely embedded
205
+ #else
238
206
  const Real *pv = ptr;
239
207
  return (sizeof(*pv) + pv->MaxPrec * sizeof(DECDIG));
208
+ #endif
240
209
  }
241
210
 
242
- #ifndef HAVE_RB_EXT_RACTOR_SAFE
243
- # undef RUBY_TYPED_FROZEN_SHAREABLE
244
- # define RUBY_TYPED_FROZEN_SHAREABLE 0
211
+ #ifndef RUBY_TYPED_THREAD_SAFE_FREE
212
+ #define RUBY_TYPED_THREAD_SAFE_FREE RUBY_TYPED_FREE_IMMEDIATELY
245
213
  #endif
246
214
 
247
215
  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
216
+ .wrap_struct_name = "BigDecimal",
217
+ .function = {
218
+ .dmark = 0,
219
+ .dfree = RUBY_DEFAULT_FREE,
220
+ .dsize = BigDecimal_memsize,
221
+ },
222
+ .flags = RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
253
223
  };
254
224
 
255
- // TypedData_Wrap_Struct may fail if there is no memory, or GC.add_stress_to_class(BigDecimal) is set.
256
- // We need to first allocate empty struct, allocate Real struct, and then set the data pointer.
257
- typedef struct { VALUE _obj; } NULL_WRAPPED_VALUE;
258
- static NULL_WRAPPED_VALUE
259
- BigDecimal_alloc_empty_struct(VALUE klass)
225
+ static VALUE
226
+ BigDecimal_allocate(size_t const internal_digits)
260
227
  {
261
- return (NULL_WRAPPED_VALUE) { TypedData_Wrap_Struct(klass, &BigDecimal_data_type, NULL) };
228
+ const size_t size = rbd_struct_size(internal_digits);
229
+ VALUE bd = rb_data_typed_object_zalloc(rb_cBigDecimal, size, &BigDecimal_data_type);
230
+ Real *vp;
231
+ TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
232
+ vp->MaxPrec = internal_digits;
233
+ RB_OBJ_FREEZE(bd);
234
+ return bd;
262
235
  }
263
236
 
264
237
  static VALUE
265
- BigDecimal_wrap_struct(NULL_WRAPPED_VALUE v, Real *real)
238
+ BigDecimal_allocate_decimal_digits(size_t const decimal_digits)
266
239
  {
267
- VALUE obj = v._obj;
268
- assert(RTYPEDDATA_DATA(obj) == NULL);
269
- RTYPEDDATA_DATA(obj) = real;
270
- RB_OBJ_FREEZE(obj);
271
- return obj;
240
+ return BigDecimal_allocate(roomof(decimal_digits, BASE_FIG));
241
+ }
242
+
243
+ static Real *
244
+ VpPtr(VALUE obj)
245
+ {
246
+ Real *vp;
247
+ TypedData_Get_Struct(obj, Real, &BigDecimal_data_type, vp);
248
+ return vp;
272
249
  }
273
250
 
274
251
  MAYBE_UNUSED(static inline BDVALUE rbd_allocate_struct_zero_wrap(int sign, size_t const digits));
@@ -276,9 +253,10 @@ MAYBE_UNUSED(static inline BDVALUE rbd_allocate_struct_zero_wrap(int sign, size_
276
253
  static BDVALUE
277
254
  rbd_allocate_struct_zero_wrap(int sign, size_t const digits)
278
255
  {
279
- NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal);
280
- Real *real = rbd_allocate_struct_zero(sign, digits);
281
- return (BDVALUE) { BigDecimal_wrap_struct(null_wrapped, real), real };
256
+ VALUE obj = BigDecimal_allocate_decimal_digits(digits);
257
+ Real *real = VpPtr(obj);
258
+ VpSetZero(real, sign);
259
+ return (BDVALUE) { obj, real };
282
260
  }
283
261
 
284
262
  static inline int
@@ -336,20 +314,11 @@ GetBDValueWithPrecInternal(VALUE v, size_t prec, int must)
336
314
  break;
337
315
  }
338
316
 
339
- #ifdef ENABLE_NUMERIC_STRING
340
- case T_STRING: {
341
- const char *c_str = StringValueCStr(v);
342
- v = rb_cstr_convert_to_BigDecimal(c_str, must);
343
- break;
344
- }
345
- #endif /* ENABLE_NUMERIC_STRING */
346
-
347
317
  default:
348
318
  goto SomeOneMayDoIt;
349
319
  }
350
320
 
351
- Real *vp;
352
- TypedData_Get_Struct(v, Real, &BigDecimal_data_type, vp);
321
+ Real *vp = VpPtr(v);
353
322
  return (NULLABLE_BDVALUE) { v, vp };
354
323
 
355
324
  SomeOneMayDoIt:
@@ -400,37 +369,6 @@ BigDecimal_double_fig(VALUE self)
400
369
  return INT2FIX(BIGDECIMAL_DOUBLE_FIGURES);
401
370
  }
402
371
 
403
- /* call-seq:
404
- * precs -> array
405
- *
406
- * Returns an Array of two Integer values that represent platform-dependent
407
- * internal storage properties.
408
- *
409
- * This method is deprecated and will be removed in the future.
410
- * Instead, use BigDecimal#n_significant_digits for obtaining the number of
411
- * significant digits in scientific notation, and BigDecimal#precision for
412
- * obtaining the number of digits in decimal notation.
413
- *
414
- */
415
-
416
- static VALUE
417
- BigDecimal_prec(VALUE self)
418
- {
419
- BDVALUE v;
420
- VALUE obj;
421
-
422
- rb_category_warn(RB_WARN_CATEGORY_DEPRECATED,
423
- "BigDecimal#precs is deprecated and will be removed in the future; "
424
- "use BigDecimal#precision instead.");
425
-
426
- v = GetBDValueMust(self);
427
- obj = rb_assoc_new(SIZET2NUM(v.real->Prec*VpBaseFig()),
428
- SIZET2NUM(v.real->MaxPrec*VpBaseFig()));
429
-
430
- RB_GC_GUARD(v.bigdecimal);
431
- return obj;
432
- }
433
-
434
372
  static void
435
373
  VpCountPrecisionAndScale(Real *p, ssize_t *out_precision, ssize_t *out_scale)
436
374
  {
@@ -734,6 +672,7 @@ BigDecimal_load(VALUE self, VALUE str)
734
672
  }
735
673
  }
736
674
  v = bdvalue_nonnullable(CreateFromString((char *)pch, self, true, true));
675
+ RB_GC_GUARD(str);
737
676
  return CheckGetValue(v);
738
677
  }
739
678
 
@@ -1027,7 +966,7 @@ BigDecimal_mode(int argc, VALUE *argv, VALUE self)
1027
966
  static size_t
1028
967
  GetAddSubPrec(Real *a, Real *b)
1029
968
  {
1030
- if (!VpIsDef(a) || !VpIsDef(b)) return (size_t)-1L;
969
+ if (VpIsZero(a) || VpIsZero(b)) return Max(a->Prec, b->Prec);
1031
970
  ssize_t min_a = a->exponent - a->Prec;
1032
971
  ssize_t min_b = b->exponent - b->Prec;
1033
972
  return Max(a->exponent, b->exponent) - Min(min_a, min_b);
@@ -1053,26 +992,18 @@ check_int_precision(VALUE v)
1053
992
  static NULLABLE_BDVALUE
1054
993
  CreateFromString(const char *str, VALUE klass, bool strict_p, bool raise_exception)
1055
994
  {
1056
- NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(klass);
1057
- Real *pv = VpAlloc(str, strict_p, raise_exception);
1058
- if (!pv) return (NULLABLE_BDVALUE) { Qnil, NULL };
1059
- return (NULLABLE_BDVALUE) { BigDecimal_wrap_struct(null_wrapped, pv), pv };
995
+ return VpAlloc(str, strict_p, raise_exception);
1060
996
  }
1061
997
 
1062
- static Real *
1063
- VpCopy(Real *pv, Real const* const x)
998
+ void
999
+ VpMemCopy(Real *pv, Real const* const x)
1064
1000
  {
1065
- assert(x != NULL);
1066
-
1067
- pv = (Real *)ruby_xrealloc(pv, rbd_struct_size(x->MaxPrec));
1068
1001
  pv->MaxPrec = x->MaxPrec;
1069
1002
  pv->Prec = x->Prec;
1070
1003
  pv->exponent = x->exponent;
1071
1004
  pv->sign = x->sign;
1072
1005
  pv->flag = x->flag;
1073
1006
  MEMCPY(pv->frac, x->frac, DECDIG, pv->MaxPrec);
1074
-
1075
- return pv;
1076
1007
  }
1077
1008
 
1078
1009
  /* Returns True if the value is Not a Number. */
@@ -1112,9 +1043,6 @@ BigDecimal_check_num(Real *p)
1112
1043
  VpCheckException(p, true);
1113
1044
  }
1114
1045
 
1115
- static VALUE BigDecimal_fix(VALUE self);
1116
- static VALUE BigDecimal_split(VALUE self);
1117
-
1118
1046
  /* Returns the value as an Integer.
1119
1047
  *
1120
1048
  * If the BigDecimal is infinity or NaN, raises FloatDomainError.
@@ -1265,7 +1193,7 @@ GetCoercePrec(Real *a, size_t prec)
1265
1193
  static VALUE
1266
1194
  BigDecimal_coerce(VALUE self, VALUE other)
1267
1195
  {
1268
- Real* pv = DATA_PTR(self);
1196
+ Real* pv = VpPtr(self);
1269
1197
  BDVALUE b = GetBDValueWithPrecMust(other, GetCoercePrec(pv, 0));
1270
1198
  return rb_assoc_new(CheckGetValue(b), self);
1271
1199
  }
@@ -1331,13 +1259,32 @@ BigDecimal_addsub_with_coerce(VALUE self, VALUE r, size_t prec, int operation)
1331
1259
  if (VpIsNaN(a.real)) return CheckGetValue(a);
1332
1260
  if (VpIsNaN(b.real)) return CheckGetValue(b);
1333
1261
 
1334
- mx = GetAddSubPrec(a.real, b.real);
1335
- if (mx == (size_t)-1L) {
1336
- /* a or b is inf */
1262
+ if (VpIsInf(a.real) || VpIsInf(b.real)) {
1337
1263
  c = NewZeroWrap(1, BASE_FIG);
1338
1264
  VpAddSub(c.real, a.real, b.real, operation);
1339
1265
  }
1340
1266
  else {
1267
+
1268
+ // Optimization when exponent difference is large
1269
+ // (1.234e+1000).add(5.678e-1000, 10) == (1.234e+1000).add(0.1e+990, 10) in every rounding mode
1270
+ if (prec && !VpIsZero(a.real) && !VpIsZero(b.real)) {
1271
+ size_t precRoom = roomof(prec, BASE_FIG);
1272
+ if (a.real->exponent - (ssize_t)Max(a.real->Prec, precRoom) - 1 > b.real->exponent) {
1273
+ BDVALUE b2 = NewZeroWrap(1, BASE_FIG);
1274
+ VpSetOne(b2.real)
1275
+ VpSetSign(b2.real, b.real->sign);
1276
+ b2.real->exponent = a.real->exponent - (ssize_t)Max(a.real->Prec, precRoom) - 1;
1277
+ b = b2;
1278
+ } else if (b.real->exponent - (ssize_t)Max(b.real->Prec, precRoom) - 1 > a.real->exponent) {
1279
+ BDVALUE a2 = NewZeroWrap(1, BASE_FIG);
1280
+ VpSetOne(a2.real)
1281
+ VpSetSign(a2.real, a.real->sign);
1282
+ a2.real->exponent = b.real->exponent - (ssize_t)Max(b.real->Prec, precRoom) - 1;
1283
+ a = a2;
1284
+ }
1285
+ }
1286
+
1287
+ mx = GetAddSubPrec(a.real, b.real);
1341
1288
  c = NewZeroWrap(1, (mx + 1) * BASE_FIG);
1342
1289
  size_t pl = VpGetPrecLimit();
1343
1290
  if (prec) VpSetPrecLimit(prec);
@@ -1714,7 +1661,7 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE
1714
1661
 
1715
1662
  if (VpIsNaN(a.real) || VpIsNaN(b.real) || (VpIsInf(a.real) && VpIsInf(b.real))) {
1716
1663
  VALUE nan = BigDecimal_nan();
1717
- *div = *mod = (NULLABLE_BDVALUE) { nan, DATA_PTR(nan) };
1664
+ *div = *mod = (NULLABLE_BDVALUE) { nan, VpPtr(nan) };
1718
1665
  goto Done;
1719
1666
  }
1720
1667
  if (VpIsZero(b.real)) {
@@ -1723,19 +1670,19 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE
1723
1670
  if (VpIsInf(a.real)) {
1724
1671
  if (VpGetSign(a.real) == VpGetSign(b.real)) {
1725
1672
  VALUE inf = BigDecimal_positive_infinity();
1726
- *div = (NULLABLE_BDVALUE) { inf, DATA_PTR(inf) };
1673
+ *div = (NULLABLE_BDVALUE) { inf, VpPtr(inf) };
1727
1674
  }
1728
1675
  else {
1729
1676
  VALUE inf = BigDecimal_negative_infinity();
1730
- *div = (NULLABLE_BDVALUE) { inf, DATA_PTR(inf) };
1677
+ *div = (NULLABLE_BDVALUE) { inf, VpPtr(inf) };
1731
1678
  }
1732
1679
  VALUE nan = BigDecimal_nan();
1733
- *mod = (NULLABLE_BDVALUE) { nan, DATA_PTR(nan) };
1680
+ *mod = (NULLABLE_BDVALUE) { nan, VpPtr(nan) };
1734
1681
  goto Done;
1735
1682
  }
1736
1683
  if (VpIsZero(a.real)) {
1737
1684
  VALUE zero = BigDecimal_positive_zero();
1738
- *div = (NULLABLE_BDVALUE) { zero, DATA_PTR(zero) };
1685
+ *div = (NULLABLE_BDVALUE) { zero, VpPtr(zero) };
1739
1686
  *mod = bdvalue_nullable(a);
1740
1687
  goto Done;
1741
1688
  }
@@ -1749,7 +1696,7 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE
1749
1696
  *mod = bdvalue_nullable(b);
1750
1697
  } else {
1751
1698
  VALUE zero = BigDecimal_positive_zero();
1752
- *div = (NULLABLE_BDVALUE) { zero, DATA_PTR(zero) };
1699
+ *div = (NULLABLE_BDVALUE) { zero, VpPtr(zero) };
1753
1700
  *mod = bdvalue_nullable(a);
1754
1701
  }
1755
1702
  goto Done;
@@ -1792,13 +1739,13 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE
1792
1739
  *div = bdvalue_nullable(dv);
1793
1740
  *mod = bdvalue_nullable(md);
1794
1741
  }
1742
+ RB_GC_GUARD(dv.bigdecimal);
1743
+ RB_GC_GUARD(md.bigdecimal);
1744
+ RB_GC_GUARD(res.bigdecimal);
1795
1745
 
1796
1746
  Done:
1797
1747
  RB_GC_GUARD(a.bigdecimal);
1798
1748
  RB_GC_GUARD(b.bigdecimal);
1799
- RB_GC_GUARD(dv.bigdecimal);
1800
- RB_GC_GUARD(md.bigdecimal);
1801
- RB_GC_GUARD(res.bigdecimal);
1802
1749
  return true;
1803
1750
  }
1804
1751
 
@@ -1867,7 +1814,7 @@ BigDecimal_divmod(VALUE self, VALUE r)
1867
1814
  NULLABLE_BDVALUE div, mod;
1868
1815
 
1869
1816
  if (BigDecimal_DoDivmod(self, r, &div, &mod, false)) {
1870
- return rb_assoc_new(CheckGetValue(bdvalue_nonnullable(div)), CheckGetValue(bdvalue_nonnullable(mod)));
1817
+ return rb_assoc_new(BigDecimal_to_i(CheckGetValue(bdvalue_nonnullable(div))), CheckGetValue(bdvalue_nonnullable(mod)));
1871
1818
  }
1872
1819
  return DoSomeOne(self,r,rb_intern("divmod"));
1873
1820
  }
@@ -2507,7 +2454,7 @@ BigDecimal_decimal_shift(VALUE self, VALUE v)
2507
2454
  prec = a.real->Prec + shiftDown;
2508
2455
  c = NewZeroWrap(1, prec * BASE_FIG);
2509
2456
  if (shift == 0) {
2510
- VpAsgn(c.real, a.real, 1);
2457
+ VpAsgn(c.real, a.real, 10);
2511
2458
  } else if (shiftDown) {
2512
2459
  DECDIG carry = 0;
2513
2460
  exponentShift++;
@@ -2593,9 +2540,7 @@ check_exception(VALUE bd)
2593
2540
  {
2594
2541
  assert(is_kind_of_BigDecimal(bd));
2595
2542
 
2596
- Real *vp;
2597
- TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
2598
- VpCheckException(vp, false);
2543
+ VpCheckException(VpPtr(bd), false);
2599
2544
 
2600
2545
  return bd;
2601
2546
  }
@@ -2603,17 +2548,19 @@ check_exception(VALUE bd)
2603
2548
  static VALUE
2604
2549
  rb_uint64_convert_to_BigDecimal(uint64_t uval)
2605
2550
  {
2606
- NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal);
2551
+ VALUE bd;
2607
2552
  Real *vp;
2608
2553
  if (uval == 0) {
2609
- vp = rbd_allocate_struct(1);
2554
+ bd = BigDecimal_allocate(1);
2555
+ vp = VpPtr(bd);
2610
2556
  vp->Prec = 1;
2611
2557
  vp->exponent = 1;
2612
2558
  VpSetZero(vp, 1);
2613
2559
  vp->frac[0] = 0;
2614
2560
  }
2615
2561
  else if (uval < BASE) {
2616
- vp = rbd_allocate_struct(1);
2562
+ bd = BigDecimal_allocate(1);
2563
+ vp = VpPtr(bd);
2617
2564
  vp->Prec = 1;
2618
2565
  vp->exponent = 1;
2619
2566
  VpSetSign(vp, 1);
@@ -2638,14 +2585,15 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval)
2638
2585
  }
2639
2586
 
2640
2587
  const size_t exp = len + ntz;
2641
- vp = rbd_allocate_struct(len);
2588
+ bd = BigDecimal_allocate(len);
2589
+ vp = VpPtr(bd);
2642
2590
  vp->Prec = len;
2643
2591
  vp->exponent = exp;
2644
2592
  VpSetSign(vp, 1);
2645
2593
  MEMCPY(vp->frac, buf + BIGDECIMAL_INT64_MAX_LENGTH - len, DECDIG, len);
2646
2594
  }
2647
2595
 
2648
- return BigDecimal_wrap_struct(null_wrapped, vp);
2596
+ return bd;
2649
2597
  }
2650
2598
 
2651
2599
  static VALUE
@@ -2654,8 +2602,7 @@ rb_int64_convert_to_BigDecimal(int64_t ival)
2654
2602
  const uint64_t uval = (ival < 0) ? (((uint64_t)-(ival+1))+1) : (uint64_t)ival;
2655
2603
  VALUE bd = rb_uint64_convert_to_BigDecimal(uval);
2656
2604
  if (ival < 0) {
2657
- Real *vp;
2658
- TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
2605
+ Real *vp = VpPtr(bd);
2659
2606
  VpSetSign(vp, -1);
2660
2607
  }
2661
2608
  return bd;
@@ -2766,7 +2713,7 @@ rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
2766
2713
  len10 = BIGDECIMAL_DOUBLE_FIGURES;
2767
2714
  }
2768
2715
  memcpy(buf, p, len10);
2769
- xfree(p);
2716
+ free(p);
2770
2717
 
2771
2718
  VALUE inum;
2772
2719
  size_t RB_UNUSED_VAR(prec) = 0;
@@ -2862,8 +2809,7 @@ rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
2862
2809
  }
2863
2810
 
2864
2811
  VALUE bd = rb_inum_convert_to_BigDecimal(inum);
2865
- Real *vp;
2866
- TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
2812
+ Real *vp = VpPtr(bd);
2867
2813
  assert(vp->Prec == prec);
2868
2814
  vp->exponent = exp;
2869
2815
 
@@ -2900,8 +2846,13 @@ rb_cstr_convert_to_BigDecimal(const char *c_str, int raise_exception)
2900
2846
  static inline VALUE
2901
2847
  rb_str_convert_to_BigDecimal(VALUE val, int raise_exception)
2902
2848
  {
2849
+ StringValue(val);
2850
+ rb_must_asciicompat(val);
2851
+ if (!raise_exception && memchr(RSTRING_PTR(val), '\0', RSTRING_LEN(val))) return Qnil;
2903
2852
  const char *c_str = StringValueCStr(val);
2904
- return rb_cstr_convert_to_BigDecimal(c_str, raise_exception);
2853
+ VALUE bd = rb_cstr_convert_to_BigDecimal(c_str, raise_exception);
2854
+ RB_GC_GUARD(val);
2855
+ return bd;
2905
2856
  }
2906
2857
 
2907
2858
  static VALUE
@@ -2929,13 +2880,15 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
2929
2880
  if (digs == SIZE_MAX)
2930
2881
  return check_exception(val);
2931
2882
 
2932
- NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal);
2933
- Real *vp;
2934
- TypedData_Get_Struct(val, Real, &BigDecimal_data_type, vp);
2935
- vp = VpCopy(NULL, vp);
2883
+ Real *vp = VpPtr(val);
2884
+
2885
+ VALUE copy = BigDecimal_allocate(vp->MaxPrec);
2886
+ Real *vp_copy = VpPtr(copy);
2887
+
2888
+ VpMemCopy(vp_copy, vp);
2889
+
2936
2890
  RB_GC_GUARD(val);
2937
2891
 
2938
- VALUE copy = BigDecimal_wrap_struct(null_wrapped, vp);
2939
2892
  /* TODO: rounding */
2940
2893
  return check_exception(copy);
2941
2894
  }
@@ -2951,7 +2904,7 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
2951
2904
  else if (RB_TYPE_P(val, T_COMPLEX)) {
2952
2905
  VALUE im = rb_complex_imag(val);
2953
2906
  if (!is_zero(im)) {
2954
- /* TODO: handle raise_exception */
2907
+ if (!raise_exception) return Qnil;
2955
2908
  rb_raise(rb_eArgError,
2956
2909
  "Unable to make a BigDecimal from non-zero imaginary number");
2957
2910
  }
@@ -3061,8 +3014,15 @@ f_BigDecimal(int argc, VALUE *argv, VALUE self)
3061
3014
  static VALUE
3062
3015
  BigDecimal_s_interpret_loosely(VALUE klass, VALUE str)
3063
3016
  {
3017
+ StringValue(str);
3018
+ rb_must_asciicompat(str);
3019
+ /* Like String#to_f, ignore everything after an embedded NUL */
3020
+ const char *p = RSTRING_PTR(str);
3021
+ const char *nul = memchr(p, '\0', RSTRING_LEN(str));
3022
+ if (nul) str = rb_str_subseq(str, 0, nul - p);
3064
3023
  char const *c_str = StringValueCStr(str);
3065
3024
  NULLABLE_BDVALUE v = CreateFromString(c_str, klass, false, true);
3025
+ RB_GC_GUARD(str);
3066
3026
  if (v.bigdecimal_or_nil == Qnil)
3067
3027
  return Qnil;
3068
3028
  else
@@ -3257,19 +3217,39 @@ BigDecimal_literal(const char *str)
3257
3217
 
3258
3218
  #ifdef BIGDECIMAL_USE_VP_TEST_METHODS
3259
3219
  VALUE
3260
- BigDecimal_vpdivd(VALUE self, VALUE r, VALUE cprec) {
3261
- BDVALUE a,b,c,d;
3220
+ BigDecimal_vpdivd_generic(VALUE self, VALUE r, VALUE cprec, void (*vpdivd_func)(Real*, Real*, Real*, Real*)) {
3221
+ BDVALUE a, b, c, d;
3262
3222
  size_t cn = NUM2INT(cprec);
3263
3223
  a = GetBDValueMust(self);
3264
3224
  b = GetBDValueMust(r);
3265
3225
  c = NewZeroWrap(1, cn * BASE_FIG);
3266
3226
  d = NewZeroWrap(1, VPDIVD_REM_PREC(a.real, b.real, c.real) * BASE_FIG);
3267
- VpDivd(c.real, d.real, a.real, b.real);
3227
+ vpdivd_func(c.real, d.real, a.real, b.real);
3268
3228
  RB_GC_GUARD(a.bigdecimal);
3269
3229
  RB_GC_GUARD(b.bigdecimal);
3270
3230
  return rb_assoc_new(c.bigdecimal, d.bigdecimal);
3271
3231
  }
3272
3232
 
3233
+ void
3234
+ VpDivdNormal(Real *c, Real *r, Real *a, Real *b) {
3235
+ VpDivd(c, r, a, b);
3236
+ }
3237
+
3238
+ VALUE
3239
+ BigDecimal_vpdivd(VALUE self, VALUE r, VALUE cprec) {
3240
+ return BigDecimal_vpdivd_generic(self, r, cprec, VpDivdNormal);
3241
+ }
3242
+
3243
+ VALUE
3244
+ BigDecimal_vpdivd_newton(VALUE self, VALUE r, VALUE cprec) {
3245
+ return BigDecimal_vpdivd_generic(self, r, cprec, VpDivdNewton);
3246
+ }
3247
+
3248
+ VALUE
3249
+ BigDecimal_newton_raphson_inverse(VALUE self, VALUE prec) {
3250
+ return newton_raphson_inverse(self, NUM2SIZET(prec));
3251
+ }
3252
+
3273
3253
  VALUE
3274
3254
  BigDecimal_vpmult(VALUE self, VALUE v) {
3275
3255
  BDVALUE a,b,c;
@@ -3281,6 +3261,23 @@ BigDecimal_vpmult(VALUE self, VALUE v) {
3281
3261
  RB_GC_GUARD(b.bigdecimal);
3282
3262
  return c.bigdecimal;
3283
3263
  }
3264
+
3265
+ VALUE
3266
+ BigDecimal_nttmult(VALUE self, VALUE v) {
3267
+ BDVALUE a,b,c;
3268
+ a = GetBDValueMust(self);
3269
+ b = GetBDValueMust(v);
3270
+ c = NewZeroWrap(1, VPMULT_RESULT_PREC(a.real, b.real) * BASE_FIG);
3271
+ ntt_multiply(a.real->Prec, b.real->Prec, a.real->frac, b.real->frac, c.real->frac);
3272
+ VpSetSign(c.real, a.real->sign * b.real->sign);
3273
+ c.real->exponent = a.real->exponent + b.real->exponent;
3274
+ c.real->Prec = a.real->Prec + b.real->Prec;
3275
+ VpNmlz(c.real);
3276
+ RB_GC_GUARD(a.bigdecimal);
3277
+ RB_GC_GUARD(b.bigdecimal);
3278
+ return c.bigdecimal;
3279
+ }
3280
+
3284
3281
  #endif /* BIGDECIMAL_USE_VP_TEST_METHODS */
3285
3282
 
3286
3283
  /* Document-class: BigDecimal
@@ -3593,7 +3590,6 @@ Init_bigdecimal(void)
3593
3590
  rb_define_const(rb_cBigDecimal, "NAN", BIGDECIMAL_LITERAL(NAN, NaN));
3594
3591
 
3595
3592
  /* instance methods */
3596
- rb_define_method(rb_cBigDecimal, "precs", BigDecimal_prec, 0);
3597
3593
  rb_define_method(rb_cBigDecimal, "precision", BigDecimal_precision, 0);
3598
3594
  rb_define_method(rb_cBigDecimal, "scale", BigDecimal_scale, 0);
3599
3595
  rb_define_method(rb_cBigDecimal, "precision_scale", BigDecimal_precision_scale, 0);
@@ -3652,7 +3648,10 @@ Init_bigdecimal(void)
3652
3648
 
3653
3649
  #ifdef BIGDECIMAL_USE_VP_TEST_METHODS
3654
3650
  rb_define_method(rb_cBigDecimal, "vpdivd", BigDecimal_vpdivd, 2);
3651
+ rb_define_method(rb_cBigDecimal, "vpdivd_newton", BigDecimal_vpdivd_newton, 2);
3652
+ rb_define_method(rb_cBigDecimal, "newton_raphson_inverse", BigDecimal_newton_raphson_inverse, 1);
3655
3653
  rb_define_method(rb_cBigDecimal, "vpmult", BigDecimal_vpmult, 1);
3654
+ rb_define_method(rb_cBigDecimal, "nttmult", BigDecimal_nttmult, 1);
3656
3655
  #endif /* BIGDECIMAL_USE_VP_TEST_METHODS */
3657
3656
 
3658
3657
  #define ROUNDING_MODE(i, name, value) \
@@ -3675,7 +3674,6 @@ Init_bigdecimal(void)
3675
3674
 
3676
3675
  #undef ROUNDING_MODE
3677
3676
 
3678
- id_to_r = rb_intern_const("to_r");
3679
3677
  id_eq = rb_intern_const("==");
3680
3678
  id_half = rb_intern_const("half");
3681
3679
 
@@ -3695,7 +3693,7 @@ Init_bigdecimal(void)
3695
3693
  static int gfDebug = 1; /* Debug switch */
3696
3694
  #endif /* BIGDECIMAL_DEBUG */
3697
3695
 
3698
- static Real *VpConstOne; /* constant 1.0 */
3696
+ static VALUE VpConstOne; /* constant 1.0 */
3699
3697
 
3700
3698
  enum op_sw {
3701
3699
  OP_SW_ADD = 1, /* + */
@@ -4096,8 +4094,9 @@ VpInit(DECDIG BaseVal)
4096
4094
  VpGetDoubleNegZero();
4097
4095
 
4098
4096
  /* Const 1.0 */
4099
- VpConstOne = NewZero(1, 1);
4100
- VpSetOne(VpConstOne);
4097
+ rb_global_variable(&VpConstOne);
4098
+ VpConstOne = NewZeroWrap(1, 1).bigdecimal;
4099
+ VpSetOne(VpPtr(VpConstOne));
4101
4100
 
4102
4101
  #ifdef BIGDECIMAL_DEBUG
4103
4102
  gnAlloc = 0;
@@ -4109,7 +4108,7 @@ VpInit(DECDIG BaseVal)
4109
4108
  VP_EXPORT Real *
4110
4109
  VpOne(void)
4111
4110
  {
4112
- return VpConstOne;
4111
+ return VpPtr(VpConstOne);
4113
4112
  }
4114
4113
 
4115
4114
  /* If exponent overflows,then raise exception or returns 0 */
@@ -4140,7 +4139,7 @@ overflow:
4140
4139
  return VpException(VP_EXCEPTION_OVERFLOW, "Exponent overflow", 0);
4141
4140
  }
4142
4141
 
4143
- Real *
4142
+ NULLABLE_BDVALUE
4144
4143
  bigdecimal_parse_special_string(const char *str)
4145
4144
  {
4146
4145
  static const struct {
@@ -4165,66 +4164,27 @@ bigdecimal_parse_special_string(const char *str)
4165
4164
  p = str + table[i].len;
4166
4165
  while (*p && ISSPACE(*p)) ++p;
4167
4166
  if (*p == '\0') {
4168
- Real *vp = rbd_allocate_struct(1);
4167
+ VALUE obj = BigDecimal_allocate(1);
4168
+ Real *vp = VpPtr(obj);
4169
4169
  switch (table[i].sign) {
4170
4170
  default:
4171
- UNREACHABLE; break;
4171
+ UNREACHABLE;
4172
+ return (NULLABLE_BDVALUE) { Qnil, NULL };
4172
4173
  case VP_SIGN_POSITIVE_INFINITE:
4173
4174
  VpSetPosInf(vp);
4174
- return vp;
4175
+ break;
4175
4176
  case VP_SIGN_NEGATIVE_INFINITE:
4176
4177
  VpSetNegInf(vp);
4177
- return vp;
4178
+ break;
4178
4179
  case VP_SIGN_NaN:
4179
4180
  VpSetNaN(vp);
4180
- return vp;
4181
+ break;
4181
4182
  }
4183
+ return (NULLABLE_BDVALUE) { obj, vp };
4182
4184
  }
4183
4185
  }
4184
4186
 
4185
- return NULL;
4186
- }
4187
-
4188
- struct VpCtoV_args {
4189
- Real *a;
4190
- const char *int_chr;
4191
- size_t ni;
4192
- const char *frac;
4193
- size_t nf;
4194
- const char *exp_chr;
4195
- size_t ne;
4196
- };
4197
-
4198
- static VALUE
4199
- call_VpCtoV(VALUE arg)
4200
- {
4201
- struct VpCtoV_args *x = (struct VpCtoV_args *)arg;
4202
- return (VALUE)VpCtoV(x->a, x->int_chr, x->ni, x->frac, x->nf, x->exp_chr, x->ne);
4203
- }
4204
-
4205
- static int
4206
- 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)
4207
- {
4208
- struct VpCtoV_args args;
4209
- int state = 0;
4210
-
4211
- args.a = a;
4212
- args.int_chr = int_chr;
4213
- args.ni = ni;
4214
- args.frac = frac;
4215
- args.nf = nf;
4216
- args.exp_chr = exp_chr;
4217
- args.ne = ne;
4218
-
4219
- VALUE result = rb_protect(call_VpCtoV, (VALUE)&args, &state);
4220
- if (state) {
4221
- if (free_on_error) {
4222
- rbd_free_struct(a);
4223
- }
4224
- rb_jump_tag(state);
4225
- }
4226
-
4227
- return (int)result;
4187
+ return (NULLABLE_BDVALUE) { Qnil, NULL };
4228
4188
  }
4229
4189
 
4230
4190
  /*
@@ -4233,25 +4193,25 @@ protected_VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size
4233
4193
  * szVal ... The value assigned(char).
4234
4194
  *
4235
4195
  * [Returns]
4236
- * Pointer to the newly allocated variable, or
4237
- * NULL be returned if memory allocation is failed,or any error.
4196
+ * NULLABLE_BDVALUE to the newly allocated variable.
4197
+ * Null is returned if memory allocation failed, or any error occured.
4238
4198
  */
4239
- VP_EXPORT Real *
4199
+ VP_EXPORT NULLABLE_BDVALUE
4240
4200
  VpAlloc(const char *szVal, int strict_p, int exc)
4241
4201
  {
4242
4202
  const char *orig_szVal = szVal;
4243
4203
  size_t i, j, ni, ipf, nf, ipe, ne, exp_seen, nalloc;
4244
4204
  char v, *psz;
4245
4205
  int sign=1;
4246
- Real *vp = NULL;
4247
4206
  VALUE buf;
4248
4207
 
4249
4208
  /* Skipping leading spaces */
4250
4209
  while (ISSPACE(*szVal)) szVal++;
4251
4210
 
4252
4211
  /* Check on Inf & NaN */
4253
- if ((vp = bigdecimal_parse_special_string(szVal)) != NULL) {
4254
- return vp;
4212
+ NULLABLE_BDVALUE special_bd = bigdecimal_parse_special_string(szVal);
4213
+ if (special_bd.real_or_null != NULL) {
4214
+ return special_bd;
4255
4215
  }
4256
4216
 
4257
4217
  /* Skip leading `#`.
@@ -4405,10 +4365,11 @@ VpAlloc(const char *szVal, int strict_p, int exc)
4405
4365
  VALUE str;
4406
4366
  invalid_value:
4407
4367
  if (!strict_p) {
4408
- return NewZero(1, 1);
4368
+ BDVALUE res = rbd_allocate_struct_zero_wrap(1, 1);
4369
+ return (NULLABLE_BDVALUE) { res.bigdecimal, res.real };
4409
4370
  }
4410
4371
  if (!exc) {
4411
- return NULL;
4372
+ return (NULLABLE_BDVALUE) { Qnil, NULL };
4412
4373
  }
4413
4374
  str = rb_str_new2(orig_szVal);
4414
4375
  rb_raise(rb_eArgError, "invalid value for BigDecimal(): \"%"PRIsVALUE"\"", str);
@@ -4416,11 +4377,12 @@ VpAlloc(const char *szVal, int strict_p, int exc)
4416
4377
 
4417
4378
  nalloc = (ni + nf + BASE_FIG - 1) / BASE_FIG + 1; /* set effective allocation */
4418
4379
  /* units for szVal[] */
4419
- vp = rbd_allocate_struct(nalloc);
4380
+ VALUE obj = BigDecimal_allocate(nalloc);
4381
+ Real *vp = VpPtr(obj);
4420
4382
  VpSetZero(vp, sign);
4421
- protected_VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne, true);
4383
+ VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne);
4422
4384
  rb_str_resize(buf, 0);
4423
- return vp;
4385
+ return (NULLABLE_BDVALUE) { obj, vp };
4424
4386
  }
4425
4387
 
4426
4388
  /*
@@ -4892,17 +4854,12 @@ VpSetPTR(Real *a, Real *b, Real *c, size_t *a_pos, size_t *b_pos, size_t *c_pos,
4892
4854
  * a0 a1 .... an * b0
4893
4855
  * +_____________________________
4894
4856
  * c0 c1 c2 ...... cl
4895
- * nc <---|
4896
- * MaxAB |--------------------|
4897
4857
  */
4898
4858
  VP_EXPORT size_t
4899
4859
  VpMult(Real *c, Real *a, Real *b)
4900
4860
  {
4901
- size_t MxIndA, MxIndB, MxIndAB;
4902
- size_t ind_c, i, ii, nc;
4903
- size_t ind_as, ind_ae, ind_bs;
4904
- DECDIG carry;
4905
- DECDIG_DBL s;
4861
+ ssize_t a_batch_max, b_batch_max;
4862
+ DECDIG_DBL batch[VPMULT_BATCH_SIZE * 2 - 1];
4906
4863
 
4907
4864
  if (!VpIsDefOP(c, a, b, OP_SW_MULT)) return 0; /* No significant digit */
4908
4865
 
@@ -4926,61 +4883,57 @@ VpMult(Real *c, Real *a, Real *b)
4926
4883
  a = b;
4927
4884
  b = w;
4928
4885
  }
4929
- MxIndA = a->Prec - 1;
4930
- MxIndB = b->Prec - 1;
4931
- MxIndAB = a->Prec + b->Prec - 1;
4932
4886
 
4933
4887
  /* set LHSV c info */
4934
4888
 
4935
4889
  c->exponent = a->exponent; /* set exponent */
4936
4890
  VpSetSign(c, VpGetSign(a) * VpGetSign(b)); /* set sign */
4937
4891
  if (!AddExponent(c, b->exponent)) return 0;
4938
- carry = 0;
4939
- nc = ind_c = MxIndAB;
4940
- memset(c->frac, 0, (nc + 1) * sizeof(DECDIG)); /* Initialize c */
4941
- c->Prec = nc + 1; /* set precision */
4942
- for (nc = 0; nc < MxIndAB; ++nc, --ind_c) {
4943
- if (nc < MxIndB) { /* The left triangle of the Fig. */
4944
- ind_as = MxIndA - nc;
4945
- ind_ae = MxIndA;
4946
- ind_bs = MxIndB;
4947
- }
4948
- else if (nc <= MxIndA) { /* The middle rectangular of the Fig. */
4949
- ind_as = MxIndA - nc;
4950
- ind_ae = MxIndA - (nc - MxIndB);
4951
- ind_bs = MxIndB;
4952
- }
4953
- else /* if (nc > MxIndA) */ { /* The right triangle of the Fig. */
4954
- ind_as = 0;
4955
- ind_ae = MxIndAB - nc - 1;
4956
- ind_bs = MxIndB - (nc - MxIndA);
4957
- }
4958
4892
 
4959
- for (i = ind_as; i <= ind_ae; ++i) {
4960
- s = (DECDIG_DBL)a->frac[i] * b->frac[ind_bs--];
4961
- carry = (DECDIG)(s / BASE);
4962
- s -= (DECDIG_DBL)carry * BASE;
4963
- c->frac[ind_c] += (DECDIG)s;
4964
- if (c->frac[ind_c] >= BASE) {
4965
- s = c->frac[ind_c] / BASE;
4966
- carry += (DECDIG)s;
4967
- c->frac[ind_c] -= (DECDIG)(s * BASE);
4893
+ if (b->Prec >= NTT_MULTIPLICATION_THRESHOLD) {
4894
+ ntt_multiply(a->Prec, b->Prec, a->frac, b->frac, c->frac);
4895
+ c->Prec = a->Prec + b->Prec;
4896
+ goto Cleanup;
4897
+ }
4898
+
4899
+ c->Prec = a->Prec + b->Prec; /* set precision */
4900
+ memset(c->frac, 0, c->Prec * sizeof(DECDIG)); /* Initialize c */
4901
+
4902
+ // Process VPMULT_BATCH_SIZE decdigits at a time to reduce the number of carry operations.
4903
+ a_batch_max = (a->Prec - 1) / VPMULT_BATCH_SIZE;
4904
+ b_batch_max = (b->Prec - 1) / VPMULT_BATCH_SIZE;
4905
+ for (ssize_t ibatch = a_batch_max; ibatch >= 0; ibatch--) {
4906
+ int isize = ibatch == a_batch_max ? (a->Prec - 1) % VPMULT_BATCH_SIZE + 1 : VPMULT_BATCH_SIZE;
4907
+ for (ssize_t jbatch = b_batch_max; jbatch >= 0; jbatch--) {
4908
+ int jsize = jbatch == b_batch_max ? (b->Prec - 1) % VPMULT_BATCH_SIZE + 1 : VPMULT_BATCH_SIZE;
4909
+ memset(batch, 0, (isize + jsize - 1) * sizeof(DECDIG_DBL));
4910
+
4911
+ // Perform multiplication without carry calculation.
4912
+ // BASE * BASE * VPMULT_BATCH_SIZE < 2**64 should be satisfied so that
4913
+ // DECDIG_DBL can hold the intermediate sum without overflow.
4914
+ for (int i = 0; i < isize; i++) {
4915
+ for (int j = 0; j < jsize; j++) {
4916
+ batch[i + j] += (DECDIG_DBL)a->frac[ibatch * VPMULT_BATCH_SIZE + i] * b->frac[jbatch * VPMULT_BATCH_SIZE + j];
4917
+ }
4968
4918
  }
4969
- if (carry) {
4970
- ii = ind_c;
4971
- while (ii-- > 0) {
4972
- c->frac[ii] += carry;
4973
- if (c->frac[ii] >= BASE) {
4974
- carry = c->frac[ii] / BASE;
4975
- c->frac[ii] -= (carry * BASE);
4976
- }
4977
- else {
4978
- break;
4979
- }
4980
- }
4981
- }
4982
- }
4919
+
4920
+ // Add the batch result to c with carry calculation.
4921
+ DECDIG_DBL carry = 0;
4922
+ for (int k = isize + jsize - 2; k >= 0; k--) {
4923
+ size_t l = (ibatch + jbatch) * VPMULT_BATCH_SIZE + k + 1;
4924
+ DECDIG_DBL s = c->frac[l] + batch[k] + carry;
4925
+ c->frac[l] = (DECDIG)(s % BASE);
4926
+ carry = (DECDIG_DBL)(s / BASE);
4927
+ }
4928
+
4929
+ // Adding carry may exceed BASE, but it won't cause overflow of DECDIG.
4930
+ // Exceeded value will be resolved in the carry operation of next (ibatch + jbatch - 1) batch.
4931
+ // WARNING: This safety strongly relies on the current nested loop execution order.
4932
+ c->frac[(ibatch + jbatch) * VPMULT_BATCH_SIZE] += (DECDIG)carry;
4933
+ }
4983
4934
  }
4935
+
4936
+ Cleanup:
4984
4937
  VpNmlz(c);
4985
4938
 
4986
4939
  Exit:
@@ -5028,6 +4981,11 @@ VpDivd(Real *c, Real *r, Real *a, Real *b)
5028
4981
 
5029
4982
  if (word_a > word_r || word_b + word_c - 2 >= word_r) goto space_error;
5030
4983
 
4984
+ if (word_c >= NEWTON_RAPHSON_DIVISION_THRESHOLD && word_b >= NEWTON_RAPHSON_DIVISION_THRESHOLD) {
4985
+ VpDivdNewton(c, r, a, b);
4986
+ goto Exit;
4987
+ }
4988
+
5031
4989
  for (i = 0; i < word_a; ++i) r->frac[i] = a->frac[i];
5032
4990
  for (i = word_a; i < word_r; ++i) r->frac[i] = 0;
5033
4991
  for (i = 0; i < word_c; ++i) c->frac[i] = 0;
@@ -5178,6 +5136,7 @@ VpNmlz(Real *a)
5178
5136
  NoVal:
5179
5137
  a->frac[0] = 0;
5180
5138
  a->Prec = 1;
5139
+ a->exponent = 0;
5181
5140
  return 0;
5182
5141
  }
5183
5142
 
@@ -5453,8 +5412,8 @@ VpSzMantissa(Real *a, char *buf, size_t buflen)
5453
5412
  while (m) {
5454
5413
  nn = e / m;
5455
5414
  if (!ZeroSup || nn) {
5456
- snprintf(buf, buflen, "%lu", (unsigned long)nn); /* The leading zero(s) */
5457
- buf += strlen(buf);
5415
+ *buf = (char)('0' + nn);
5416
+ buf++;
5458
5417
  /* as 0.00xx will be ignored. */
5459
5418
  ZeroSup = 0; /* Set to print succeeding zeros */
5460
5419
  }
@@ -5506,10 +5465,22 @@ VpToSpecialString(Real *a, char *buf, size_t buflen, int fPlus)
5506
5465
  return 0;
5507
5466
  }
5508
5467
 
5468
+ #define ULLTOA_BUFFER_SIZE 20
5469
+ static size_t Vp_ulltoa(unsigned long long number, char *buf)
5470
+ {
5471
+ static const char digits[] = "0123456789";
5472
+ char* tmp = buf;
5473
+
5474
+ do *tmp-- = digits[number % 10]; while (number /= 10);
5475
+ return buf - tmp;
5476
+ }
5477
+
5509
5478
  VP_EXPORT void
5510
5479
  VpToString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus)
5511
5480
  /* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
5512
5481
  {
5482
+ char ulltoa_buf[ULLTOA_BUFFER_SIZE];
5483
+ char *ulltoa_buf_end = ulltoa_buf + ULLTOA_BUFFER_SIZE;
5513
5484
  size_t i, n, ZeroSup;
5514
5485
  DECDIG shift, m, e, nn;
5515
5486
  char *p = buf;
@@ -5549,10 +5520,9 @@ VpToString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus)
5549
5520
  while (m) {
5550
5521
  nn = e / m;
5551
5522
  if (!ZeroSup || nn) {
5552
- /* The reading zero(s) */
5553
- size_t n = (size_t)snprintf(p, plen, "%lu", (unsigned long)nn);
5554
- if (n > plen) goto overflow;
5555
- ADVANCE(n);
5523
+ *p = (char)('0' + nn);
5524
+ ADVANCE(1);
5525
+
5556
5526
  /* as 0.00xx will be ignored. */
5557
5527
  ZeroSup = 0; /* Set to print succeeding zeros */
5558
5528
  }
@@ -5571,7 +5541,22 @@ VpToString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus)
5571
5541
  *(--p) = '\0';
5572
5542
  ++plen;
5573
5543
  }
5574
- snprintf(p, plen, "e%"PRIdSIZE, ex);
5544
+ *p = 'e';
5545
+ ADVANCE(1);
5546
+
5547
+ if (ex < 0) {
5548
+ *p = '-';
5549
+ ADVANCE(1);
5550
+ ex = -ex;
5551
+ }
5552
+
5553
+ size_t ex_n = Vp_ulltoa(ex, ulltoa_buf_end - 1);
5554
+ if (ex_n > plen) goto overflow;
5555
+ MEMCPY(p, ulltoa_buf_end - ex_n, char, ex_n);
5556
+ ADVANCE(ex_n);
5557
+ *p = '\0';
5558
+ ADVANCE(1);
5559
+
5575
5560
  if (fFmt) VpFormatSt(buf, fFmt);
5576
5561
 
5577
5562
  overflow:
@@ -6036,7 +6021,7 @@ VpMidRound(Real *y, unsigned short f, ssize_t nf)
6036
6021
  y->frac[ix] = div;
6037
6022
  VpNmlz(y);
6038
6023
  }
6039
- if (exptoadd > 0) {
6024
+ if (exptoadd > 0 && !VpIsZero(y)) {
6040
6025
  y->exponent += (SIGNED_VALUE)(exptoadd / BASE_FIG);
6041
6026
  exptoadd %= (ssize_t)BASE_FIG;
6042
6027
  for (i = 0; i < exptoadd; i++) {
@@ -6166,7 +6151,7 @@ VpFrac(Real *y, Real *x)
6166
6151
  size_t my, ind_y, ind_x;
6167
6152
 
6168
6153
  if (!VpHasVal(x)) {
6169
- VpAsgn(y, x, 1);
6154
+ VpAsgn(y, x, 10);
6170
6155
  goto Exit;
6171
6156
  }
6172
6157
 
@@ -6175,7 +6160,7 @@ VpFrac(Real *y, Real *x)
6175
6160
  goto Exit;
6176
6161
  }
6177
6162
  else if (x->exponent <= 0) {
6178
- VpAsgn(y, x, 1);
6163
+ VpAsgn(y, x, 10);
6179
6164
  goto Exit;
6180
6165
  }
6181
6166