bigdecimal 4.1.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4356e024d6f3144b6d788ea32ad1832dab72203834e2a91484edc75e7c6c08c
4
- data.tar.gz: 7847d9f86e86e3f9be90a51fbfa6e6045bcdb6c1886ab1559b3fe2ebfe107356
3
+ metadata.gz: 937928a0baf09e4c2e5bf45b2fa608e1aa20601cf7cd27a8ebaf679874e6196f
4
+ data.tar.gz: a053c6e868c7e55ad9a35262bfd645314fd908dbae5969935c44530a124cfcc4
5
5
  SHA512:
6
- metadata.gz: 4bc5ce4f4fe5a041d83d8eec1d662bf35c50f8e2d2e317d659f833a9a2ee15280b6e8c3a4fa09dbaf177ad2daf34c9c15076819fb1bd4bf22b72efa8a1e9c6f5
7
- data.tar.gz: d1a8c638bd997d8a882f9d2409dca84853cb6e2af768c79143554a6a316d5dd2a99f7b247fcf496079bba33faec31f93fcc4d78df5913b2350eb9655b91a2834
6
+ metadata.gz: 4a4a41830aab00fd3bef041c64f9f6357891706ca11004696c5d815b7582f6f4f2cbd00a606900cfe4a5bce57585d46523e25da8085c2bf18aa95bbe9afd27ec
7
+ data.tar.gz: fe5b587431cca8921613367e236a9844fd3c65f8f53d8b2ae16fe1ed54aac455bfff9a50783256ec2d9e0f5523bc01a3eb9ce0ffc16482cded6026cc03b8c2c0
@@ -33,10 +33,12 @@
33
33
  #include "div.h"
34
34
  #include "static_assert.h"
35
35
 
36
- #define BIGDECIMAL_VERSION "4.1.0"
36
+ #define BIGDECIMAL_VERSION "4.1.1"
37
37
 
38
- #define NTT_MULTIPLICATION_THRESHOLD 100
39
- #define NEWTON_RAPHSON_DIVISION_THRESHOLD 200
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
40
42
  #define SIGNED_VALUE_MAX INTPTR_MAX
41
43
  #define SIGNED_VALUE_MIN INTPTR_MIN
42
44
  #define MUL_OVERFLOW_SIGNED_VALUE_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, SIGNED_VALUE_MIN, SIGNED_VALUE_MAX)
@@ -77,11 +79,6 @@ static struct {
77
79
  uint8_t mode;
78
80
  } rbd_rounding_modes[RBD_NUM_ROUNDING_MODES];
79
81
 
80
- typedef struct {
81
- VALUE bigdecimal_or_nil;
82
- Real *real_or_null;
83
- } NULLABLE_BDVALUE;
84
-
85
82
  static inline BDVALUE
86
83
  bdvalue_nonnullable(NULLABLE_BDVALUE v)
87
84
  {
@@ -157,42 +154,6 @@ rbd_struct_size(size_t const internal_digits)
157
154
  return offsetof(Real, frac) + frac_len * sizeof(DECDIG);
158
155
  }
159
156
 
160
- static inline Real *
161
- rbd_allocate_struct(size_t const internal_digits)
162
- {
163
- size_t const size = rbd_struct_size(internal_digits);
164
- Real *real = ruby_xcalloc(1, size);
165
- atomic_allocation_count_inc();
166
- real->MaxPrec = internal_digits;
167
- return real;
168
- }
169
-
170
- static inline Real *
171
- rbd_allocate_struct_decimal_digits(size_t const decimal_digits)
172
- {
173
- return rbd_allocate_struct(roomof(decimal_digits, BASE_FIG));
174
- }
175
-
176
- static void
177
- rbd_free_struct(Real *real)
178
- {
179
- if (real != NULL) {
180
- check_allocation_count_nonzero();
181
- ruby_xfree(real);
182
- atomic_allocation_count_dec_nounderflow();
183
- }
184
- }
185
-
186
- MAYBE_UNUSED(static inline Real * rbd_allocate_struct_zero(int sign, size_t const digits));
187
- #define NewZero rbd_allocate_struct_zero
188
- static inline Real *
189
- rbd_allocate_struct_zero(int sign, size_t const digits)
190
- {
191
- Real *real = rbd_allocate_struct_decimal_digits(digits);
192
- VpSetZero(real, sign);
193
- return real;
194
- }
195
-
196
157
  /*
197
158
  * ================== Ruby Interface part ==========================
198
159
  */
@@ -207,7 +168,6 @@ static void VpCheckException(Real *p, bool always);
207
168
  static VALUE CheckGetValue(BDVALUE v);
208
169
  static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v);
209
170
  static int VpLimitRound(Real *c, size_t ixDigit);
210
- static Real *VpCopy(Real *pv, Real const* const x);
211
171
  static int VPrint(FILE *fp,const char *cntl_chr,Real *a);
212
172
 
213
173
  /*
@@ -222,49 +182,67 @@ static VALUE BigDecimal_negative_zero(void);
222
182
  static VALUE BigDecimal_addsub_with_coerce(VALUE self, VALUE r, size_t prec, int operation);
223
183
  static VALUE BigDecimal_mult_with_coerce(VALUE self, VALUE r, size_t prec);
224
184
 
225
- static void
226
- BigDecimal_delete(void *pv)
227
- {
228
- rbd_free_struct(pv);
229
- }
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
230
200
 
231
201
  static size_t
232
202
  BigDecimal_memsize(const void *ptr)
233
203
  {
204
+ #ifdef HAVE_RUBY_TYPED_EMBEDDABLE
205
+ return 0; // Entirely embedded
206
+ #else
234
207
  const Real *pv = ptr;
235
208
  return (sizeof(*pv) + pv->MaxPrec * sizeof(DECDIG));
236
- }
237
-
238
- #ifndef HAVE_RB_EXT_RACTOR_SAFE
239
- # undef RUBY_TYPED_FROZEN_SHAREABLE
240
- # define RUBY_TYPED_FROZEN_SHAREABLE 0
241
209
  #endif
210
+ }
242
211
 
243
212
  static const rb_data_type_t BigDecimal_data_type = {
244
- "BigDecimal",
245
- { 0, BigDecimal_delete, BigDecimal_memsize, },
246
- #ifdef RUBY_TYPED_FREE_IMMEDIATELY
247
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED
248
- #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,
249
220
  };
250
221
 
251
- // TypedData_Wrap_Struct may fail if there is no memory, or GC.add_stress_to_class(BigDecimal) is set.
252
- // We need to first allocate empty struct, allocate Real struct, and then set the data pointer.
253
- typedef struct { VALUE _obj; } NULL_WRAPPED_VALUE;
254
- static NULL_WRAPPED_VALUE
255
- BigDecimal_alloc_empty_struct(VALUE klass)
222
+ static VALUE
223
+ BigDecimal_allocate(size_t const internal_digits)
256
224
  {
257
- return (NULL_WRAPPED_VALUE) { TypedData_Wrap_Struct(klass, &BigDecimal_data_type, NULL) };
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;
258
232
  }
259
233
 
260
234
  static VALUE
261
- BigDecimal_wrap_struct(NULL_WRAPPED_VALUE v, Real *real)
235
+ BigDecimal_allocate_decimal_digits(size_t const decimal_digits)
262
236
  {
263
- VALUE obj = v._obj;
264
- assert(RTYPEDDATA_DATA(obj) == NULL);
265
- RTYPEDDATA_DATA(obj) = real;
266
- RB_OBJ_FREEZE(obj);
267
- return obj;
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;
268
246
  }
269
247
 
270
248
  MAYBE_UNUSED(static inline BDVALUE rbd_allocate_struct_zero_wrap(int sign, size_t const digits));
@@ -272,9 +250,10 @@ MAYBE_UNUSED(static inline BDVALUE rbd_allocate_struct_zero_wrap(int sign, size_
272
250
  static BDVALUE
273
251
  rbd_allocate_struct_zero_wrap(int sign, size_t const digits)
274
252
  {
275
- NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal);
276
- Real *real = rbd_allocate_struct_zero(sign, digits);
277
- return (BDVALUE) { BigDecimal_wrap_struct(null_wrapped, real), real };
253
+ VALUE obj = BigDecimal_allocate_decimal_digits(digits);
254
+ Real *real = VpPtr(obj);
255
+ VpSetZero(real, sign);
256
+ return (BDVALUE) { obj, real };
278
257
  }
279
258
 
280
259
  static inline int
@@ -336,8 +315,7 @@ GetBDValueWithPrecInternal(VALUE v, size_t prec, int must)
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:
@@ -1010,26 +988,18 @@ check_int_precision(VALUE v)
1010
988
  static NULLABLE_BDVALUE
1011
989
  CreateFromString(const char *str, VALUE klass, bool strict_p, bool raise_exception)
1012
990
  {
1013
- NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(klass);
1014
- Real *pv = VpAlloc(str, strict_p, raise_exception);
1015
- if (!pv) return (NULLABLE_BDVALUE) { Qnil, NULL };
1016
- return (NULLABLE_BDVALUE) { BigDecimal_wrap_struct(null_wrapped, pv), pv };
991
+ return VpAlloc(str, strict_p, raise_exception);
1017
992
  }
1018
993
 
1019
- static Real *
1020
- VpCopy(Real *pv, Real const* const x)
994
+ void
995
+ VpMemCopy(Real *pv, Real const* const x)
1021
996
  {
1022
- assert(x != NULL);
1023
-
1024
- pv = (Real *)ruby_xrealloc(pv, rbd_struct_size(x->MaxPrec));
1025
997
  pv->MaxPrec = x->MaxPrec;
1026
998
  pv->Prec = x->Prec;
1027
999
  pv->exponent = x->exponent;
1028
1000
  pv->sign = x->sign;
1029
1001
  pv->flag = x->flag;
1030
1002
  MEMCPY(pv->frac, x->frac, DECDIG, pv->MaxPrec);
1031
-
1032
- return pv;
1033
1003
  }
1034
1004
 
1035
1005
  /* Returns True if the value is Not a Number. */
@@ -1219,7 +1189,7 @@ GetCoercePrec(Real *a, size_t prec)
1219
1189
  static VALUE
1220
1190
  BigDecimal_coerce(VALUE self, VALUE other)
1221
1191
  {
1222
- Real* pv = DATA_PTR(self);
1192
+ Real* pv = VpPtr(self);
1223
1193
  BDVALUE b = GetBDValueWithPrecMust(other, GetCoercePrec(pv, 0));
1224
1194
  return rb_assoc_new(CheckGetValue(b), self);
1225
1195
  }
@@ -1687,7 +1657,7 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE
1687
1657
 
1688
1658
  if (VpIsNaN(a.real) || VpIsNaN(b.real) || (VpIsInf(a.real) && VpIsInf(b.real))) {
1689
1659
  VALUE nan = BigDecimal_nan();
1690
- *div = *mod = (NULLABLE_BDVALUE) { nan, DATA_PTR(nan) };
1660
+ *div = *mod = (NULLABLE_BDVALUE) { nan, VpPtr(nan) };
1691
1661
  goto Done;
1692
1662
  }
1693
1663
  if (VpIsZero(b.real)) {
@@ -1696,19 +1666,19 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE
1696
1666
  if (VpIsInf(a.real)) {
1697
1667
  if (VpGetSign(a.real) == VpGetSign(b.real)) {
1698
1668
  VALUE inf = BigDecimal_positive_infinity();
1699
- *div = (NULLABLE_BDVALUE) { inf, DATA_PTR(inf) };
1669
+ *div = (NULLABLE_BDVALUE) { inf, VpPtr(inf) };
1700
1670
  }
1701
1671
  else {
1702
1672
  VALUE inf = BigDecimal_negative_infinity();
1703
- *div = (NULLABLE_BDVALUE) { inf, DATA_PTR(inf) };
1673
+ *div = (NULLABLE_BDVALUE) { inf, VpPtr(inf) };
1704
1674
  }
1705
1675
  VALUE nan = BigDecimal_nan();
1706
- *mod = (NULLABLE_BDVALUE) { nan, DATA_PTR(nan) };
1676
+ *mod = (NULLABLE_BDVALUE) { nan, VpPtr(nan) };
1707
1677
  goto Done;
1708
1678
  }
1709
1679
  if (VpIsZero(a.real)) {
1710
1680
  VALUE zero = BigDecimal_positive_zero();
1711
- *div = (NULLABLE_BDVALUE) { zero, DATA_PTR(zero) };
1681
+ *div = (NULLABLE_BDVALUE) { zero, VpPtr(zero) };
1712
1682
  *mod = bdvalue_nullable(a);
1713
1683
  goto Done;
1714
1684
  }
@@ -1722,7 +1692,7 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE
1722
1692
  *mod = bdvalue_nullable(b);
1723
1693
  } else {
1724
1694
  VALUE zero = BigDecimal_positive_zero();
1725
- *div = (NULLABLE_BDVALUE) { zero, DATA_PTR(zero) };
1695
+ *div = (NULLABLE_BDVALUE) { zero, VpPtr(zero) };
1726
1696
  *mod = bdvalue_nullable(a);
1727
1697
  }
1728
1698
  goto Done;
@@ -2566,9 +2536,7 @@ check_exception(VALUE bd)
2566
2536
  {
2567
2537
  assert(is_kind_of_BigDecimal(bd));
2568
2538
 
2569
- Real *vp;
2570
- TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
2571
- VpCheckException(vp, false);
2539
+ VpCheckException(VpPtr(bd), false);
2572
2540
 
2573
2541
  return bd;
2574
2542
  }
@@ -2576,17 +2544,19 @@ check_exception(VALUE bd)
2576
2544
  static VALUE
2577
2545
  rb_uint64_convert_to_BigDecimal(uint64_t uval)
2578
2546
  {
2579
- NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal);
2547
+ VALUE bd;
2580
2548
  Real *vp;
2581
2549
  if (uval == 0) {
2582
- vp = rbd_allocate_struct(1);
2550
+ bd = BigDecimal_allocate(1);
2551
+ vp = VpPtr(bd);
2583
2552
  vp->Prec = 1;
2584
2553
  vp->exponent = 1;
2585
2554
  VpSetZero(vp, 1);
2586
2555
  vp->frac[0] = 0;
2587
2556
  }
2588
2557
  else if (uval < BASE) {
2589
- vp = rbd_allocate_struct(1);
2558
+ bd = BigDecimal_allocate(1);
2559
+ vp = VpPtr(bd);
2590
2560
  vp->Prec = 1;
2591
2561
  vp->exponent = 1;
2592
2562
  VpSetSign(vp, 1);
@@ -2611,14 +2581,15 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval)
2611
2581
  }
2612
2582
 
2613
2583
  const size_t exp = len + ntz;
2614
- vp = rbd_allocate_struct(len);
2584
+ bd = BigDecimal_allocate(len);
2585
+ vp = VpPtr(bd);
2615
2586
  vp->Prec = len;
2616
2587
  vp->exponent = exp;
2617
2588
  VpSetSign(vp, 1);
2618
2589
  MEMCPY(vp->frac, buf + BIGDECIMAL_INT64_MAX_LENGTH - len, DECDIG, len);
2619
2590
  }
2620
2591
 
2621
- return BigDecimal_wrap_struct(null_wrapped, vp);
2592
+ return bd;
2622
2593
  }
2623
2594
 
2624
2595
  static VALUE
@@ -2627,8 +2598,7 @@ rb_int64_convert_to_BigDecimal(int64_t ival)
2627
2598
  const uint64_t uval = (ival < 0) ? (((uint64_t)-(ival+1))+1) : (uint64_t)ival;
2628
2599
  VALUE bd = rb_uint64_convert_to_BigDecimal(uval);
2629
2600
  if (ival < 0) {
2630
- Real *vp;
2631
- TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
2601
+ Real *vp = VpPtr(bd);
2632
2602
  VpSetSign(vp, -1);
2633
2603
  }
2634
2604
  return bd;
@@ -2835,8 +2805,7 @@ rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
2835
2805
  }
2836
2806
 
2837
2807
  VALUE bd = rb_inum_convert_to_BigDecimal(inum);
2838
- Real *vp;
2839
- TypedData_Get_Struct(bd, Real, &BigDecimal_data_type, vp);
2808
+ Real *vp = VpPtr(bd);
2840
2809
  assert(vp->Prec == prec);
2841
2810
  vp->exponent = exp;
2842
2811
 
@@ -2902,13 +2871,15 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
2902
2871
  if (digs == SIZE_MAX)
2903
2872
  return check_exception(val);
2904
2873
 
2905
- NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal);
2906
- Real *vp;
2907
- TypedData_Get_Struct(val, Real, &BigDecimal_data_type, vp);
2908
- 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
+
2909
2881
  RB_GC_GUARD(val);
2910
2882
 
2911
- VALUE copy = BigDecimal_wrap_struct(null_wrapped, vp);
2912
2883
  /* TODO: rounding */
2913
2884
  return check_exception(copy);
2914
2885
  }
@@ -3707,7 +3678,7 @@ Init_bigdecimal(void)
3707
3678
  static int gfDebug = 1; /* Debug switch */
3708
3679
  #endif /* BIGDECIMAL_DEBUG */
3709
3680
 
3710
- static Real *VpConstOne; /* constant 1.0 */
3681
+ static VALUE VpConstOne; /* constant 1.0 */
3711
3682
 
3712
3683
  enum op_sw {
3713
3684
  OP_SW_ADD = 1, /* + */
@@ -4108,8 +4079,9 @@ VpInit(DECDIG BaseVal)
4108
4079
  VpGetDoubleNegZero();
4109
4080
 
4110
4081
  /* Const 1.0 */
4111
- VpConstOne = NewZero(1, 1);
4112
- VpSetOne(VpConstOne);
4082
+ rb_global_variable(&VpConstOne);
4083
+ VpConstOne = NewZeroWrap(1, 1).bigdecimal;
4084
+ VpSetOne(VpPtr(VpConstOne));
4113
4085
 
4114
4086
  #ifdef BIGDECIMAL_DEBUG
4115
4087
  gnAlloc = 0;
@@ -4121,7 +4093,7 @@ VpInit(DECDIG BaseVal)
4121
4093
  VP_EXPORT Real *
4122
4094
  VpOne(void)
4123
4095
  {
4124
- return VpConstOne;
4096
+ return VpPtr(VpConstOne);
4125
4097
  }
4126
4098
 
4127
4099
  /* If exponent overflows,then raise exception or returns 0 */
@@ -4152,7 +4124,7 @@ overflow:
4152
4124
  return VpException(VP_EXCEPTION_OVERFLOW, "Exponent overflow", 0);
4153
4125
  }
4154
4126
 
4155
- Real *
4127
+ NULLABLE_BDVALUE
4156
4128
  bigdecimal_parse_special_string(const char *str)
4157
4129
  {
4158
4130
  static const struct {
@@ -4177,66 +4149,27 @@ bigdecimal_parse_special_string(const char *str)
4177
4149
  p = str + table[i].len;
4178
4150
  while (*p && ISSPACE(*p)) ++p;
4179
4151
  if (*p == '\0') {
4180
- Real *vp = rbd_allocate_struct(1);
4152
+ VALUE obj = BigDecimal_allocate(1);
4153
+ Real *vp = VpPtr(obj);
4181
4154
  switch (table[i].sign) {
4182
4155
  default:
4183
- UNREACHABLE; break;
4156
+ UNREACHABLE;
4157
+ return (NULLABLE_BDVALUE) { Qnil, NULL };
4184
4158
  case VP_SIGN_POSITIVE_INFINITE:
4185
4159
  VpSetPosInf(vp);
4186
- return vp;
4160
+ break;
4187
4161
  case VP_SIGN_NEGATIVE_INFINITE:
4188
4162
  VpSetNegInf(vp);
4189
- return vp;
4163
+ break;
4190
4164
  case VP_SIGN_NaN:
4191
4165
  VpSetNaN(vp);
4192
- return vp;
4166
+ break;
4193
4167
  }
4168
+ return (NULLABLE_BDVALUE) { obj, vp };
4194
4169
  }
4195
4170
  }
4196
4171
 
4197
- return NULL;
4198
- }
4199
-
4200
- struct VpCtoV_args {
4201
- Real *a;
4202
- const char *int_chr;
4203
- size_t ni;
4204
- const char *frac;
4205
- size_t nf;
4206
- const char *exp_chr;
4207
- size_t ne;
4208
- };
4209
-
4210
- static VALUE
4211
- call_VpCtoV(VALUE arg)
4212
- {
4213
- struct VpCtoV_args *x = (struct VpCtoV_args *)arg;
4214
- return (VALUE)VpCtoV(x->a, x->int_chr, x->ni, x->frac, x->nf, x->exp_chr, x->ne);
4215
- }
4216
-
4217
- static int
4218
- 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)
4219
- {
4220
- struct VpCtoV_args args;
4221
- int state = 0;
4222
-
4223
- args.a = a;
4224
- args.int_chr = int_chr;
4225
- args.ni = ni;
4226
- args.frac = frac;
4227
- args.nf = nf;
4228
- args.exp_chr = exp_chr;
4229
- args.ne = ne;
4230
-
4231
- VALUE result = rb_protect(call_VpCtoV, (VALUE)&args, &state);
4232
- if (state) {
4233
- if (free_on_error) {
4234
- rbd_free_struct(a);
4235
- }
4236
- rb_jump_tag(state);
4237
- }
4238
-
4239
- return (int)result;
4172
+ return (NULLABLE_BDVALUE) { Qnil, NULL };
4240
4173
  }
4241
4174
 
4242
4175
  /*
@@ -4245,25 +4178,25 @@ protected_VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size
4245
4178
  * szVal ... The value assigned(char).
4246
4179
  *
4247
4180
  * [Returns]
4248
- * Pointer to the newly allocated variable, or
4249
- * 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.
4250
4183
  */
4251
- VP_EXPORT Real *
4184
+ VP_EXPORT NULLABLE_BDVALUE
4252
4185
  VpAlloc(const char *szVal, int strict_p, int exc)
4253
4186
  {
4254
4187
  const char *orig_szVal = szVal;
4255
4188
  size_t i, j, ni, ipf, nf, ipe, ne, exp_seen, nalloc;
4256
4189
  char v, *psz;
4257
4190
  int sign=1;
4258
- Real *vp = NULL;
4259
4191
  VALUE buf;
4260
4192
 
4261
4193
  /* Skipping leading spaces */
4262
4194
  while (ISSPACE(*szVal)) szVal++;
4263
4195
 
4264
4196
  /* Check on Inf & NaN */
4265
- if ((vp = bigdecimal_parse_special_string(szVal)) != NULL) {
4266
- return vp;
4197
+ NULLABLE_BDVALUE special_bd = bigdecimal_parse_special_string(szVal);
4198
+ if (special_bd.real_or_null != NULL) {
4199
+ return special_bd;
4267
4200
  }
4268
4201
 
4269
4202
  /* Skip leading `#`.
@@ -4417,10 +4350,11 @@ VpAlloc(const char *szVal, int strict_p, int exc)
4417
4350
  VALUE str;
4418
4351
  invalid_value:
4419
4352
  if (!strict_p) {
4420
- return NewZero(1, 1);
4353
+ BDVALUE res = rbd_allocate_struct_zero_wrap(1, 1);
4354
+ return (NULLABLE_BDVALUE) { res.bigdecimal, res.real };
4421
4355
  }
4422
4356
  if (!exc) {
4423
- return NULL;
4357
+ return (NULLABLE_BDVALUE) { Qnil, NULL };
4424
4358
  }
4425
4359
  str = rb_str_new2(orig_szVal);
4426
4360
  rb_raise(rb_eArgError, "invalid value for BigDecimal(): \"%"PRIsVALUE"\"", str);
@@ -4428,11 +4362,12 @@ VpAlloc(const char *szVal, int strict_p, int exc)
4428
4362
 
4429
4363
  nalloc = (ni + nf + BASE_FIG - 1) / BASE_FIG + 1; /* set effective allocation */
4430
4364
  /* units for szVal[] */
4431
- vp = rbd_allocate_struct(nalloc);
4365
+ VALUE obj = BigDecimal_allocate(nalloc);
4366
+ Real *vp = VpPtr(obj);
4432
4367
  VpSetZero(vp, sign);
4433
- protected_VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne, true);
4368
+ VpCtoV(vp, psz, ni, psz + ipf, nf, psz + ipe, ne);
4434
4369
  rb_str_resize(buf, 0);
4435
- return vp;
4370
+ return (NULLABLE_BDVALUE) { obj, vp };
4436
4371
  }
4437
4372
 
4438
4373
  /*
@@ -4904,17 +4839,12 @@ VpSetPTR(Real *a, Real *b, Real *c, size_t *a_pos, size_t *b_pos, size_t *c_pos,
4904
4839
  * a0 a1 .... an * b0
4905
4840
  * +_____________________________
4906
4841
  * c0 c1 c2 ...... cl
4907
- * nc <---|
4908
- * MaxAB |--------------------|
4909
4842
  */
4910
4843
  VP_EXPORT size_t
4911
4844
  VpMult(Real *c, Real *a, Real *b)
4912
4845
  {
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;
4846
+ ssize_t a_batch_max, b_batch_max;
4847
+ DECDIG_DBL batch[VPMULT_BATCH_SIZE * 2 - 1];
4918
4848
 
4919
4849
  if (!VpIsDefOP(c, a, b, OP_SW_MULT)) return 0; /* No significant digit */
4920
4850
 
@@ -4938,9 +4868,6 @@ VpMult(Real *c, Real *a, Real *b)
4938
4868
  a = b;
4939
4869
  b = w;
4940
4870
  }
4941
- MxIndA = a->Prec - 1;
4942
- MxIndB = b->Prec - 1;
4943
- MxIndAB = a->Prec + b->Prec - 1;
4944
4871
 
4945
4872
  /* set LHSV c info */
4946
4873
 
@@ -4954,51 +4881,41 @@ VpMult(Real *c, Real *a, Real *b)
4954
4881
  goto Cleanup;
4955
4882
  }
4956
4883
 
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
- }
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
+ }
4903
+ }
4977
4904
 
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);
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);
4987
4912
  }
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
- }
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
+ }
5002
4919
  }
5003
4920
 
5004
4921
  Cleanup:
@@ -168,6 +168,11 @@ typedef struct {
168
168
  Real *real;
169
169
  } BDVALUE;
170
170
 
171
+ typedef struct {
172
+ VALUE bigdecimal_or_nil;
173
+ Real *real_or_null;
174
+ } NULLABLE_BDVALUE;
175
+
171
176
  /*
172
177
  * ------------------
173
178
  * EXPORTables.
@@ -194,7 +199,7 @@ VP_EXPORT unsigned short VpSetRoundMode(unsigned short n);
194
199
  VP_EXPORT int VpException(unsigned short f,const char *str,int always);
195
200
  VP_EXPORT size_t VpNumOfChars(Real *vp,const char *pszFmt);
196
201
  VP_EXPORT size_t VpInit(DECDIG BaseVal);
197
- VP_EXPORT Real *VpAlloc(const char *szVal, int strict_p, int exc);
202
+ VP_EXPORT NULLABLE_BDVALUE VpAlloc(const char *szVal, int strict_p, int exc);
198
203
  VP_EXPORT size_t VpAsgn(Real *c, Real *a, int isw);
199
204
  VP_EXPORT size_t VpAddSub(Real *c,Real *a,Real *b,int operation);
200
205
  VP_EXPORT size_t VpMult(Real *c,Real *a,Real *b);
@@ -46,6 +46,10 @@ have_func("rb_opts_exception_p", "ruby.h")
46
46
  have_func("rb_category_warn", "ruby.h")
47
47
  have_const("RB_WARN_CATEGORY_DEPRECATED", "ruby.h")
48
48
 
49
+ if RUBY_ENGINE == "ruby"
50
+ have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3
51
+ end
52
+
49
53
  if File.file?(File.expand_path('../lib/bigdecimal.rb', __FILE__))
50
54
  bigdecimal_rb = "$(srcdir)/lib/bigdecimal.rb"
51
55
  else
@@ -144,7 +144,7 @@ module BigMath
144
144
  x = -x if neg = x < 0
145
145
  ex = x.exponent / 3
146
146
  x = x._decimal_shift(-3 * ex)
147
- y = BigDecimal(Math.cbrt(x.to_f), 0)
147
+ y = BigDecimal(Math.cbrt(BigDecimal::Internal.fast_to_f(x)), 0)
148
148
  BigDecimal::Internal.newton_loop(prec + BigDecimal::Internal::EXTRA_PREC) do |p|
149
149
  y = (2 * y + x.div(y, p).div(y, p)).div(3, p)
150
150
  end
@@ -304,7 +304,7 @@ module BigMath
304
304
 
305
305
  # Solve tan(y) - x = 0 with Newton's method
306
306
  # Repeat: y -= (tan(y) - x) * cos(y)**2
307
- y = BigDecimal(Math.atan(x.to_f), 0)
307
+ y = BigDecimal(Math.atan(BigDecimal::Internal.fast_to_f(x)), 0)
308
308
  BigDecimal::Internal.newton_loop(n) do |p|
309
309
  s = sin(y, p)
310
310
  c = (1 - s * s).sqrt(p)
@@ -605,7 +605,7 @@ module BigMath
605
605
  return BigDecimal(1) if x > 5000000000 # erf(5000000000) > 1 - 1e-10000000000000000000
606
606
 
607
607
  if x > 8
608
- xf = x.to_f
608
+ xf = BigDecimal::Internal.fast_to_f(x)
609
609
  log10_erfc = -xf ** 2 / Math.log(10) - Math.log10(xf * Math::PI ** 0.5)
610
610
  erfc_prec = [prec + log10_erfc.ceil, 1].max
611
611
  erfc = _erfc_asymptotic(x, erfc_prec)
@@ -647,7 +647,7 @@ module BigMath
647
647
  # erfc(x) = 1 - erf(x) < exp(-x**2)/x/sqrt(pi)
648
648
  # Precision of erf(x) needs about log10(exp(-x**2)/x/sqrt(pi)) extra digits
649
649
  log10 = 2.302585092994046
650
- xf = x.to_f
650
+ xf = BigDecimal::Internal.fast_to_f(x)
651
651
  high_prec = prec + BigDecimal::Internal::EXTRA_PREC + ((xf**2 + Math.log(xf) + Math.log(Math::PI)/2) / log10).ceil
652
652
  BigDecimal(1).sub(erf(x, high_prec), prec)
653
653
  end
@@ -698,7 +698,7 @@ module BigMath
698
698
  # sqrt(2)/2 + k*log(k) - k - 2*k*log(x) < -prec*log(10)
699
699
  # and the left side is minimized when k = x**2.
700
700
  prec += BigDecimal::Internal::EXTRA_PREC
701
- xf = x.to_f
701
+ xf = BigDecimal::Internal.fast_to_f(x)
702
702
  kmax = (1..(xf ** 2).floor).bsearch do |k|
703
703
  Math.log(2) / 2 + k * Math.log(k) - k - 2 * k * Math.log(xf) < -prec * Math.log(10)
704
704
  end
data/lib/bigdecimal.rb CHANGED
@@ -76,9 +76,18 @@ class BigDecimal
76
76
  end
77
77
  end
78
78
 
79
+ # Fast and rough conversion to float for mathematical calculations.
80
+ # Bigdecimal#to_f is slow when n_significant_digits is large.
81
+ # This is because to_f internally converts BigDecimal to String
82
+ # to get the exact nearest float representation.
83
+ # TODO: Remove this workaround when BigDecimal#to_f is optimized.
84
+ def self.fast_to_f(x) # :nodoc:
85
+ x.n_significant_digits < 40 ? x.to_f : x.mult(1, 20).to_f
86
+ end
87
+
79
88
  # Calculates Math.log(x.to_f) considering large or small exponent
80
89
  def self.float_log(x) # :nodoc:
81
- Math.log(x._decimal_shift(-x.exponent).to_f) + x.exponent * Math.log(10)
90
+ Math.log(fast_to_f(x._decimal_shift(-x.exponent))) + x.exponent * Math.log(10)
82
91
  end
83
92
 
84
93
  # Calculating Taylor series sum using binary splitting method
@@ -268,7 +277,7 @@ class BigDecimal
268
277
 
269
278
  ex = exponent / 2
270
279
  x = _decimal_shift(-2 * ex)
271
- y = BigDecimal(Math.sqrt(x.to_f), 0)
280
+ y = BigDecimal(Math.sqrt(BigDecimal::Internal.fast_to_f(x)), 0)
272
281
  Internal.newton_loop(prec + BigDecimal::Internal::EXTRA_PREC) do |p|
273
282
  y = y.add(x.div(y, p), p).div(2, p)
274
283
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bigdecimal
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenta Murata