bigdecimal 4.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 937928a0baf09e4c2e5bf45b2fa608e1aa20601cf7cd27a8ebaf679874e6196f
4
- data.tar.gz: a053c6e868c7e55ad9a35262bfd645314fd908dbae5969935c44530a124cfcc4
3
+ metadata.gz: 1e3b015d4eafc3c72fda5a95b949fb4413a8fa068cbdad6b4ab9c8f18244dba6
4
+ data.tar.gz: d6f6639c3560978f79e43d3943881e9ad5fb01a9b03cf98c0c056f8f18614cc1
5
5
  SHA512:
6
- metadata.gz: 4a4a41830aab00fd3bef041c64f9f6357891706ca11004696c5d815b7582f6f4f2cbd00a606900cfe4a5bce57585d46523e25da8085c2bf18aa95bbe9afd27ec
7
- data.tar.gz: fe5b587431cca8921613367e236a9844fd3c65f8f53d8b2ae16fe1ed54aac455bfff9a50783256ec2d9e0f5523bc01a3eb9ce0ffc16482cded6026cc03b8c2c0
6
+ metadata.gz: b1ce1badb6a615c8a5576e2e5f5a1861dc3ff9e97b0a37b8788470c47e3f2b18329edabcf904e5ad8215ef8b0805a3d02220ed5cc7c856ec9da7231913b2bc80
7
+ data.tar.gz: 11d06bb5706fc5034cb012f49479095a5d17eabcec8c011956010e13032f228a29b9421abd1fa3f56b7db83b41dc43ecf54bb7b0d60353508c396c169c27ebee
data/bigdecimal.gemspec CHANGED
@@ -29,6 +29,8 @@ Gem::Specification.new do |s|
29
29
  lib/bigdecimal/jacobian.rb
30
30
  lib/bigdecimal/ludcmp.rb
31
31
  lib/bigdecimal/math.rb
32
+ lib/bigdecimal/math/erf.rb
33
+ lib/bigdecimal/math/gamma.rb
32
34
  lib/bigdecimal/newton.rb
33
35
  lib/bigdecimal/util.rb
34
36
  sample/linear.rb
@@ -33,7 +33,7 @@
33
33
  #include "div.h"
34
34
  #include "static_assert.h"
35
35
 
36
- #define BIGDECIMAL_VERSION "4.1.1"
36
+ #define BIGDECIMAL_VERSION "4.1.3"
37
37
 
38
38
  /* Make sure VPMULT_BATCH_SIZE*BASE*BASE does not overflow DECDIG_DBL */
39
39
  #define VPMULT_BATCH_SIZE 16
@@ -68,7 +68,6 @@ static ID id_banker;
68
68
  static ID id_ceiling;
69
69
  static ID id_ceil;
70
70
  static ID id_floor;
71
- static ID id_to_r;
72
71
  static ID id_eq;
73
72
  static ID id_half;
74
73
 
@@ -209,6 +208,10 @@ BigDecimal_memsize(const void *ptr)
209
208
  #endif
210
209
  }
211
210
 
211
+ #ifndef RUBY_TYPED_THREAD_SAFE_FREE
212
+ #define RUBY_TYPED_THREAD_SAFE_FREE RUBY_TYPED_FREE_IMMEDIATELY
213
+ #endif
214
+
212
215
  static const rb_data_type_t BigDecimal_data_type = {
213
216
  .wrap_struct_name = "BigDecimal",
214
217
  .function = {
@@ -216,7 +219,7 @@ static const rb_data_type_t BigDecimal_data_type = {
216
219
  .dfree = RUBY_DEFAULT_FREE,
217
220
  .dsize = BigDecimal_memsize,
218
221
  },
219
- .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
222
+ .flags = RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
220
223
  };
221
224
 
222
225
  static VALUE
@@ -669,6 +672,7 @@ BigDecimal_load(VALUE self, VALUE str)
669
672
  }
670
673
  }
671
674
  v = bdvalue_nonnullable(CreateFromString((char *)pch, self, true, true));
675
+ RB_GC_GUARD(str);
672
676
  return CheckGetValue(v);
673
677
  }
674
678
 
@@ -1735,13 +1739,13 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE
1735
1739
  *div = bdvalue_nullable(dv);
1736
1740
  *mod = bdvalue_nullable(md);
1737
1741
  }
1742
+ RB_GC_GUARD(dv.bigdecimal);
1743
+ RB_GC_GUARD(md.bigdecimal);
1744
+ RB_GC_GUARD(res.bigdecimal);
1738
1745
 
1739
1746
  Done:
1740
1747
  RB_GC_GUARD(a.bigdecimal);
1741
1748
  RB_GC_GUARD(b.bigdecimal);
1742
- RB_GC_GUARD(dv.bigdecimal);
1743
- RB_GC_GUARD(md.bigdecimal);
1744
- RB_GC_GUARD(res.bigdecimal);
1745
1749
  return true;
1746
1750
  }
1747
1751
 
@@ -2709,7 +2713,7 @@ rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
2709
2713
  len10 = BIGDECIMAL_DOUBLE_FIGURES;
2710
2714
  }
2711
2715
  memcpy(buf, p, len10);
2712
- xfree(p);
2716
+ free(p);
2713
2717
 
2714
2718
  VALUE inum;
2715
2719
  size_t RB_UNUSED_VAR(prec) = 0;
@@ -2842,8 +2846,13 @@ rb_cstr_convert_to_BigDecimal(const char *c_str, int raise_exception)
2842
2846
  static inline VALUE
2843
2847
  rb_str_convert_to_BigDecimal(VALUE val, int raise_exception)
2844
2848
  {
2849
+ StringValue(val);
2850
+ rb_must_asciicompat(val);
2851
+ if (!raise_exception && memchr(RSTRING_PTR(val), '\0', RSTRING_LEN(val))) return Qnil;
2845
2852
  const char *c_str = StringValueCStr(val);
2846
- 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;
2847
2856
  }
2848
2857
 
2849
2858
  static VALUE
@@ -2895,7 +2904,7 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
2895
2904
  else if (RB_TYPE_P(val, T_COMPLEX)) {
2896
2905
  VALUE im = rb_complex_imag(val);
2897
2906
  if (!is_zero(im)) {
2898
- /* TODO: handle raise_exception */
2907
+ if (!raise_exception) return Qnil;
2899
2908
  rb_raise(rb_eArgError,
2900
2909
  "Unable to make a BigDecimal from non-zero imaginary number");
2901
2910
  }
@@ -3005,8 +3014,15 @@ f_BigDecimal(int argc, VALUE *argv, VALUE self)
3005
3014
  static VALUE
3006
3015
  BigDecimal_s_interpret_loosely(VALUE klass, VALUE str)
3007
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);
3008
3023
  char const *c_str = StringValueCStr(str);
3009
3024
  NULLABLE_BDVALUE v = CreateFromString(c_str, klass, false, true);
3025
+ RB_GC_GUARD(str);
3010
3026
  if (v.bigdecimal_or_nil == Qnil)
3011
3027
  return Qnil;
3012
3028
  else
@@ -3658,7 +3674,6 @@ Init_bigdecimal(void)
3658
3674
 
3659
3675
  #undef ROUNDING_MODE
3660
3676
 
3661
- id_to_r = rb_intern_const("to_r");
3662
3677
  id_eq = rb_intern_const("==");
3663
3678
  id_half = rb_intern_const("half");
3664
3679
 
@@ -5121,6 +5136,7 @@ VpNmlz(Real *a)
5121
5136
  NoVal:
5122
5137
  a->frac[0] = 0;
5123
5138
  a->Prec = 1;
5139
+ a->exponent = 0;
5124
5140
  return 0;
5125
5141
  }
5126
5142
 
@@ -5396,8 +5412,8 @@ VpSzMantissa(Real *a, char *buf, size_t buflen)
5396
5412
  while (m) {
5397
5413
  nn = e / m;
5398
5414
  if (!ZeroSup || nn) {
5399
- snprintf(buf, buflen, "%lu", (unsigned long)nn); /* The leading zero(s) */
5400
- buf += strlen(buf);
5415
+ *buf = (char)('0' + nn);
5416
+ buf++;
5401
5417
  /* as 0.00xx will be ignored. */
5402
5418
  ZeroSup = 0; /* Set to print succeeding zeros */
5403
5419
  }
@@ -5449,10 +5465,22 @@ VpToSpecialString(Real *a, char *buf, size_t buflen, int fPlus)
5449
5465
  return 0;
5450
5466
  }
5451
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
+
5452
5478
  VP_EXPORT void
5453
5479
  VpToString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus)
5454
5480
  /* fPlus = 0: default, 1: set ' ' before digits, 2: set '+' before digits. */
5455
5481
  {
5482
+ char ulltoa_buf[ULLTOA_BUFFER_SIZE];
5483
+ char *ulltoa_buf_end = ulltoa_buf + ULLTOA_BUFFER_SIZE;
5456
5484
  size_t i, n, ZeroSup;
5457
5485
  DECDIG shift, m, e, nn;
5458
5486
  char *p = buf;
@@ -5492,10 +5520,9 @@ VpToString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus)
5492
5520
  while (m) {
5493
5521
  nn = e / m;
5494
5522
  if (!ZeroSup || nn) {
5495
- /* The reading zero(s) */
5496
- size_t n = (size_t)snprintf(p, plen, "%lu", (unsigned long)nn);
5497
- if (n > plen) goto overflow;
5498
- ADVANCE(n);
5523
+ *p = (char)('0' + nn);
5524
+ ADVANCE(1);
5525
+
5499
5526
  /* as 0.00xx will be ignored. */
5500
5527
  ZeroSup = 0; /* Set to print succeeding zeros */
5501
5528
  }
@@ -5514,7 +5541,22 @@ VpToString(Real *a, char *buf, size_t buflen, size_t fFmt, int fPlus)
5514
5541
  *(--p) = '\0';
5515
5542
  ++plen;
5516
5543
  }
5517
- 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
+
5518
5560
  if (fFmt) VpFormatSt(buf, fFmt);
5519
5561
 
5520
5562
  overflow:
@@ -5979,7 +6021,7 @@ VpMidRound(Real *y, unsigned short f, ssize_t nf)
5979
6021
  y->frac[ix] = div;
5980
6022
  VpNmlz(y);
5981
6023
  }
5982
- if (exptoadd > 0) {
6024
+ if (exptoadd > 0 && !VpIsZero(y)) {
5983
6025
  y->exponent += (SIGNED_VALUE)(exptoadd / BASE_FIG);
5984
6026
  exptoadd %= (ssize_t)BASE_FIG;
5985
6027
  for (i = 0; i < exptoadd; i++) {
@@ -267,21 +267,21 @@ VP_EXPORT inline BDVALUE rbd_allocate_struct_zero_wrap(int sign, size_t const di
267
267
  #define VpIsPosZero(a) ((a)->sign==VP_SIGN_POSITIVE_ZERO)
268
268
  #define VpIsNegZero(a) ((a)->sign==VP_SIGN_NEGATIVE_ZERO)
269
269
  #define VpIsZero(a) (VpIsPosZero(a) || VpIsNegZero(a))
270
- #define VpSetPosZero(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_POSITIVE_ZERO)
271
- #define VpSetNegZero(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_NEGATIVE_ZERO)
270
+ #define VpSetPosZero(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->exponent=0,(a)->sign=VP_SIGN_POSITIVE_ZERO)
271
+ #define VpSetNegZero(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->exponent=0,(a)->sign=VP_SIGN_NEGATIVE_ZERO)
272
272
  #define VpSetZero(a,s) (void)(((s)>0)?VpSetPosZero(a):VpSetNegZero(a))
273
273
 
274
274
  /* NaN */
275
275
  #define VpIsNaN(a) ((a)->sign==VP_SIGN_NaN)
276
- #define VpSetNaN(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_NaN)
276
+ #define VpSetNaN(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->exponent=0,(a)->sign=VP_SIGN_NaN)
277
277
 
278
278
  /* Infinity */
279
279
  #define VpIsPosInf(a) ((a)->sign==VP_SIGN_POSITIVE_INFINITE)
280
280
  #define VpIsNegInf(a) ((a)->sign==VP_SIGN_NEGATIVE_INFINITE)
281
281
  #define VpIsInf(a) (VpIsPosInf(a) || VpIsNegInf(a))
282
282
  #define VpIsDef(a) ( !(VpIsNaN(a)||VpIsInf(a)) )
283
- #define VpSetPosInf(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_POSITIVE_INFINITE)
284
- #define VpSetNegInf(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->sign=VP_SIGN_NEGATIVE_INFINITE)
283
+ #define VpSetPosInf(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->exponent=0,(a)->sign=VP_SIGN_POSITIVE_INFINITE)
284
+ #define VpSetNegInf(a) ((a)->frac[0]=0,(a)->Prec=1,(a)->exponent=0,(a)->sign=VP_SIGN_NEGATIVE_INFINITE)
285
285
  #define VpSetInf(a,s) (void)(((s)>0)?VpSetPosInf(a):VpSetNegInf(a))
286
286
  #define VpHasVal(a) (a->frac[0])
287
287
  #define VpIsOne(a) ((a->Prec==1)&&(a->frac[0]==1)&&(a->exponent==1))
data/ext/bigdecimal/div.h CHANGED
@@ -1,18 +1,45 @@
1
+ struct vp_settings {
2
+ size_t prec_limit;
3
+ unsigned short rounding_mode;
4
+ };
5
+
6
+ static VALUE
7
+ restore_vp_settings(VALUE saved_ptr)
8
+ {
9
+ struct vp_settings *saved = (struct vp_settings *)saved_ptr;
10
+ VpSetPrecLimit(saved->prec_limit);
11
+ VpSetRoundMode(saved->rounding_mode);
12
+ return Qnil;
13
+ }
14
+
1
15
  // Calculate the inverse of x using the Newton-Raphson method.
16
+ // Assumes no precision limit and ROUND_HALF_UP. See VpDivdNewton.
2
17
  static VALUE
3
18
  newton_raphson_inverse(VALUE x, size_t prec) {
4
19
  BDVALUE bdone = NewZeroWrap(1, 1);
5
20
  VpSetOne(bdone.real);
6
21
  VALUE one = bdone.bigdecimal;
7
22
 
8
- // Initial approximation in 2 digits
23
+ // Initial approximation: 10^18 / (leading 9 digits of x), calculated in 64-bit integer.
24
+ // Truncating x to 9 digits and the quotient to an integer keep the relative error below 1.2e-8,
25
+ // so it has at least 7 correct digits.
26
+ const size_t initial_digits = 7;
27
+ const DECDIG_DBL base_sq = (DECDIG_DBL)BIGDECIMAL_BASE * BIGDECIMAL_BASE;
9
28
  BDVALUE bdx = GetBDValueMust(x);
29
+ DECDIG_DBL x_lead = (DECDIG_DBL)bdx.real->frac[0] * BIGDECIMAL_BASE + (bdx.real->Prec >= 2 ? bdx.real->frac[1] : 0);
30
+ int shift = 0;
31
+ while (x_lead < base_sq / 10) {
32
+ x_lead *= 10;
33
+ shift++;
34
+ }
35
+ // Dividing base_sq - 1 instead of base_sq keeps the quotient below 10 * BASE,
36
+ // so that frac[0] of inv0 never reaches BASE.
37
+ DECDIG_DBL inv_lead = (base_sq - 1) / (x_lead / BIGDECIMAL_BASE);
38
+ for (int i = 0; i < shift; i++) inv_lead *= 10;
10
39
  BDVALUE inv0 = NewZeroWrap(1, 2 * BIGDECIMAL_COMPONENT_FIGURES);
11
40
  VpSetOne(inv0.real);
12
- DECDIG_DBL numerator = (DECDIG_DBL)BIGDECIMAL_BASE * 100;
13
- DECDIG_DBL denominator = (DECDIG_DBL)bdx.real->frac[0] * 100 + (DECDIG_DBL)(bdx.real->Prec >= 2 ? bdx.real->frac[1] : 0) * 100 / BIGDECIMAL_BASE;
14
- inv0.real->frac[0] = (DECDIG)(numerator / denominator);
15
- inv0.real->frac[1] = (DECDIG)((numerator % denominator) * (BIGDECIMAL_BASE / 100) / denominator * 100);
41
+ inv0.real->frac[0] = (DECDIG)(inv_lead / BIGDECIMAL_BASE);
42
+ inv0.real->frac[1] = (DECDIG)(inv_lead % BIGDECIMAL_BASE);
16
43
  inv0.real->Prec = 2;
17
44
  inv0.real->exponent = 1 - bdx.real->exponent;
18
45
  VpNmlz(inv0.real);
@@ -22,14 +49,21 @@ newton_raphson_inverse(VALUE x, size_t prec) {
22
49
  int bl = 1;
23
50
  while (((size_t)1 << bl) < prec) bl++;
24
51
 
52
+ // Each iteration doubles the number of correct digits, and the rounding error of
53
+ // the previous iteration is squared into the new digits. Margin digits keep the
54
+ // squared error below the last digit, otherwise it can grow over iterations.
55
+ const size_t margin = 4;
25
56
  for (int i = bl; i >= 0; i--) {
26
- size_t n = (prec >> i) + 2;
57
+ size_t n = (prec >> i) + margin;
27
58
  if (n > prec) n = prec;
59
+ // inv0 already has this precision. The last iteration is kept to round inv to prec digits.
60
+ if (n <= initial_digits && i > 0) continue;
28
61
  // Newton-Raphson iteration: inv_next = inv + inv * (1 - x * inv)
62
+ // (1 - x * inv) is about 10^(-n/2), so calculating it in n/2 digits is enough for inv_next in n digits.
29
63
  VALUE one_minus_x_inv = BigDecimal_sub2(
30
64
  one,
31
65
  BigDecimal_mult(BigDecimal_mult2(x, one, SIZET2NUM(n + 1)), inv),
32
- SIZET2NUM(SIZET2NUM(n / 2))
66
+ SIZET2NUM(n / 2 + margin)
33
67
  );
34
68
  inv = BigDecimal_add2(
35
69
  inv,
@@ -59,15 +93,11 @@ divmod_by_inv_mul(VALUE x, VALUE y, VALUE inv, VALUE *res_div, VALUE *res_mod) {
59
93
 
60
94
  static void
61
95
  slice_copy(DECDIG *dest, Real *src, size_t rshift, size_t length) {
62
- ssize_t start = src->exponent - rshift - length;
63
- if (start >= (ssize_t)src->Prec) return;
64
- if (start < 0) {
65
- dest -= start;
66
- length += start;
67
- start = 0;
68
- }
69
- size_t max_length = src->Prec - start;
70
- memcpy(dest, src->frac + start, Min(length, max_length) * sizeof(DECDIG));
96
+ ssize_t start = src->exponent - (ssize_t)rshift - (ssize_t)length;
97
+ ssize_t from = Max(start, 0);
98
+ ssize_t to = Min(start + (ssize_t)length, (ssize_t)src->Prec);
99
+ if (from >= to) return;
100
+ memcpy(dest + (from - start), src->frac + from, (size_t)(to - from) * sizeof(DECDIG));
71
101
  }
72
102
 
73
103
  /* Calculates divmod using Newton-Raphson method.
@@ -101,28 +131,28 @@ divmod_newton(VALUE x, VALUE y, VALUE *div_out, VALUE *mod_out) {
101
131
  BDVALUE div_result = NewZeroWrap(1, BIGDECIMAL_COMPONENT_FIGURES * (num_blocks * block_figs + 1));
102
132
  BDVALUE bdx = GetBDValueMust(x);
103
133
 
104
- VALUE mod = BigDecimal_fix(BigDecimal_decimal_shift(x, SSIZET2NUM(-num_blocks * block_digits)));
105
- for (ssize_t i = num_blocks - 1; i >= 0; i--) {
134
+ VALUE mod = BigDecimal_fix(BigDecimal_decimal_shift(x, SSIZET2NUM(-(ssize_t)(num_blocks * block_digits))));
135
+ for (ssize_t i = (ssize_t)(num_blocks - 1); i >= 0; i--) {
106
136
  memset(divident.real->frac, 0, (y_figs + block_figs) * sizeof(DECDIG));
107
137
 
108
138
  BDVALUE bdmod = GetBDValueMust(mod);
109
139
  slice_copy(divident.real->frac, bdmod.real, 0, y_figs);
110
- slice_copy(divident.real->frac + y_figs, bdx.real, i * block_figs, block_figs);
140
+ slice_copy(divident.real->frac + y_figs, bdx.real, (size_t)i * block_figs, block_figs);
111
141
  RB_GC_GUARD(bdmod.bigdecimal);
112
142
 
113
143
  VpSetSign(divident.real, 1);
114
- divident.real->exponent = y_figs + block_figs;
144
+ divident.real->exponent = (ssize_t)(y_figs + block_figs);
115
145
  divident.real->Prec = y_figs + block_figs;
116
146
  VpNmlz(divident.real);
117
147
 
118
148
  VALUE div;
119
149
  divmod_by_inv_mul(divident.bigdecimal, y, yinv, &div, &mod);
120
150
  BDVALUE bddiv = GetBDValueMust(div);
121
- slice_copy(div_result.real->frac + (num_blocks - i - 1) * block_figs, bddiv.real, 0, block_figs + 1);
151
+ slice_copy(div_result.real->frac + (num_blocks - (size_t)i - 1) * block_figs, bddiv.real, 0, block_figs + 1);
122
152
  RB_GC_GUARD(bddiv.bigdecimal);
123
153
  }
124
154
  VpSetSign(div_result.real, 1);
125
- div_result.real->exponent = num_blocks * block_figs + 1;
155
+ div_result.real->exponent = (ssize_t)(num_blocks * block_figs + 1);
126
156
  div_result.real->Prec = num_blocks * block_figs + 1;
127
157
  VpNmlz(div_result.real);
128
158
  RB_GC_GUARD(bdx.bigdecimal);
@@ -132,11 +162,15 @@ divmod_newton(VALUE x, VALUE y, VALUE *div_out, VALUE *mod_out) {
132
162
  *mod_out = mod;
133
163
  }
134
164
 
165
+ struct vp_divd_newton_args {
166
+ Real *c, *r, *a, *b;
167
+ };
168
+
135
169
  static VALUE
136
170
  VpDivdNewtonInner(VALUE args_ptr)
137
171
  {
138
- Real **args = (Real**)args_ptr;
139
- Real *c = args[0], *r = args[1], *a = args[2], *b = args[3];
172
+ struct vp_divd_newton_args *args = (struct vp_divd_newton_args *)args_ptr;
173
+ Real *c = args->c, *r = args->r, *a = args->a, *b = args->b;
140
174
  BDVALUE a2, b2, c2, r2;
141
175
  VALUE div, mod, a2_frac = Qnil;
142
176
  size_t div_prec = c->MaxPrec - 1;
@@ -148,8 +182,8 @@ VpDivdNewtonInner(VALUE args_ptr)
148
182
  VpAsgn(b2.real, b, 1);
149
183
  VpSetSign(a2.real, 1);
150
184
  VpSetSign(b2.real, 1);
151
- a2.real->exponent = base_prec + div_prec;
152
- b2.real->exponent = base_prec;
185
+ a2.real->exponent = (ssize_t)(base_prec + div_prec);
186
+ b2.real->exponent = (ssize_t)base_prec;
153
187
 
154
188
  if ((ssize_t)a2.real->Prec > a2.real->exponent) {
155
189
  a2_frac = BigDecimal_frac(a2.bigdecimal);
@@ -162,31 +196,24 @@ VpDivdNewtonInner(VALUE args_ptr)
162
196
  r2 = GetBDValueMust(mod);
163
197
  VpAsgn(c, c2.real, VpGetSign(a) * VpGetSign(b));
164
198
  VpAsgn(r, r2.real, VpGetSign(a));
165
- AddExponent(c, a->exponent);
166
- AddExponent(c, -b->exponent);
167
- AddExponent(c, -div_prec);
168
- AddExponent(r, a->exponent);
169
- AddExponent(r, -base_prec - div_prec);
170
- RB_GC_GUARD(a2.bigdecimal);
199
+ if (!VpIsZero(c)) AddExponent(c, a->exponent - b->exponent - (ssize_t)div_prec);
200
+ if (!VpIsZero(r)) AddExponent(r, a->exponent - (ssize_t)(base_prec + div_prec));
171
201
  RB_GC_GUARD(a2.bigdecimal);
202
+ RB_GC_GUARD(b2.bigdecimal);
172
203
  RB_GC_GUARD(c2.bigdecimal);
173
204
  RB_GC_GUARD(r2.bigdecimal);
174
205
  return Qnil;
175
206
  }
176
207
 
177
- static VALUE
178
- ensure_restore_prec_limit(VALUE limit)
179
- {
180
- VpSetPrecLimit(NUM2SIZET(limit));
181
- return Qnil;
182
- }
183
-
208
+ // Newton-Raphson iteration converges from below, so a rounding mode that rounds toward zero
209
+ // (ROUND_DOWN, ROUND_FLOOR) adds error in the same direction at every iteration.
210
+ // Calculate with ROUND_HALF_UP and without precision limit, and restore them even if an exception is raised.
184
211
  static void
185
212
  VpDivdNewton(Real *c, Real *r, Real *a, Real *b)
186
213
  {
187
- Real *args[4] = {c, r, a, b};
188
- size_t pl = VpGetPrecLimit();
214
+ struct vp_divd_newton_args args = {c, r, a, b};
215
+ struct vp_settings saved = {VpGetPrecLimit(), VpGetRoundMode()};
189
216
  VpSetPrecLimit(0);
190
- // Ensure restoring prec limit because some methods used in VpDivdNewtonInner may raise an exception
191
- rb_ensure(VpDivdNewtonInner, (VALUE)args, ensure_restore_prec_limit, SIZET2NUM(pl));
217
+ VpSetRoundMode(VP_ROUND_HALF_UP);
218
+ rb_ensure(VpDivdNewtonInner, (VALUE)&args, restore_vp_settings, (VALUE)&saved);
192
219
  }