bigdecimal 4.1.2 → 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: 922981a841d43e8269ac09725757bd02512ba596903b2da2ab2458e842957833
4
- data.tar.gz: 34b59473beafbb51888ef2700b0c44ee721025952c1b578c4affa6cb60e91d90
3
+ metadata.gz: 1e3b015d4eafc3c72fda5a95b949fb4413a8fa068cbdad6b4ab9c8f18244dba6
4
+ data.tar.gz: d6f6639c3560978f79e43d3943881e9ad5fb01a9b03cf98c0c056f8f18614cc1
5
5
  SHA512:
6
- metadata.gz: 00e7aedfc9b90c0e82297880c750010e37b8aa427c122343418bbbc3b656a94583a818b0980d2a91b9a7c22aa9c5cc26d621536c33f0d5ec70b87bf9585feed7
7
- data.tar.gz: 0b54105583917681020ed7d451f9a39c68f9805e9079b48cc83b7031d43ab5e070945dd4899d73e99925022f4a3462c6f9f277062af0281b1455ed16b0a76f49
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.2"
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
 
@@ -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
 
@@ -6005,7 +6021,7 @@ VpMidRound(Real *y, unsigned short f, ssize_t nf)
6005
6021
  y->frac[ix] = div;
6006
6022
  VpNmlz(y);
6007
6023
  }
6008
- if (exptoadd > 0) {
6024
+ if (exptoadd > 0 && !VpIsZero(y)) {
6009
6025
  y->exponent += (SIGNED_VALUE)(exptoadd / BASE_FIG);
6010
6026
  exptoadd %= (ssize_t)BASE_FIG;
6011
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,
@@ -60,14 +94,10 @@ divmod_by_inv_mul(VALUE x, VALUE y, VALUE inv, VALUE *res_div, VALUE *res_mod) {
60
94
  static void
61
95
  slice_copy(DECDIG *dest, Real *src, size_t rshift, size_t length) {
62
96
  ssize_t start = src->exponent - (ssize_t)rshift - (ssize_t)length;
63
- if (start >= (ssize_t)src->Prec) return;
64
- if (start < 0) {
65
- dest -= start;
66
- length -= (size_t)(-start);
67
- start = 0;
68
- }
69
- size_t max_length = (size_t)((ssize_t)src->Prec - start);
70
- memcpy(dest, src->frac + start, Min(length, max_length) * sizeof(DECDIG));
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.
@@ -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;
@@ -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, -(ssize_t)div_prec);
168
- AddExponent(r, a->exponent);
169
- AddExponent(r, -(ssize_t)(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
  }
@@ -0,0 +1,291 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BigMath
4
+ # Bit-burst implementation of BigMath.erf and BigMath.erfc.
5
+ #
6
+ # Both functions share the same incremental update: given erf(x0+...+xk) (or erfc),
7
+ # extend to erf(x0+...+xk+x_{k+1}) by adding (or, for erfc, subtracting) the Taylor
8
+ # expansion of the difference function
9
+ # g(t) := (erf(t + a) - erf(a)) * exp(a**2) * sqrt(pi) / 2 with a = x0+...+xk
10
+ # which satisfies the homogeneous ODE g''(t) + 2*(t+a)*g'(t) = 0.
11
+ # Each step uses binary splitting on the 3-term recurrence of g's Taylor coefficients;
12
+ # split widths x1, x2, ... double in digits, giving quasi-linear total cost.
13
+ #
14
+ # Only the bit-burst seed differs between the two:
15
+ # erf : seed = erf(x0) via Taylor expansion at 0
16
+ # erfc : seed = erfc(x0) via asymptotic expansion (requires x0 large enough;
17
+ # returns nil if asymptotic cannot reach the requested precision, in which
18
+ # case erfc(x) is recovered from 1 - erf(x) with extra digits to absorb
19
+ # cancellation)
20
+ #
21
+ # Edge cases (after symmetry erf(-x) = -erf(x)):
22
+ # x == 0 : erf = 0
23
+ # x > 5e9 : erf = 1, erfc underflows
24
+ # x < 0.5 (erfc only) : compute via 1 - erf to avoid unnecessary work
25
+ module Erf # :nodoc:
26
+
27
+ # Calculates erf with given precision.
28
+ def self.erf(x, prec)
29
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :erf)
30
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erf)
31
+ return BigDecimal::Internal.nan_computation_result if x.nan?
32
+ return BigDecimal(x.infinite?) if x.infinite?
33
+ return BigDecimal(0) if x == 0
34
+ return -erf(-x, prec) if x < 0
35
+ return BigDecimal(1) if x > 5000000000 # erf(5000000000) > 1 - 1e-10000000000000000000
36
+ if x > 8
37
+ xf = x.to_f
38
+ log10_erfc = -xf ** 2 / Math.log(10) - Math.log10(xf * Math::PI ** 0.5)
39
+ erfc_prec = [prec + log10_erfc.ceil, 1].max
40
+ erfc = erfc_bit_burst(x, erfc_prec + BigDecimal::Internal::EXTRA_PREC)
41
+ return BigDecimal(1).sub(erfc, prec) if erfc
42
+ end
43
+
44
+ erf_bit_burst(x, prec + BigDecimal::Internal::EXTRA_PREC).mult(1, prec)
45
+ end
46
+
47
+ # Calculates erfc with given precision.
48
+ def self.erfc(x, prec)
49
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :erfc)
50
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erfc)
51
+ return BigDecimal::Internal.nan_computation_result if x.nan?
52
+ return BigDecimal(1 - x.infinite?) if x.infinite?
53
+ return BigDecimal(1).sub(erf(x, prec + BigDecimal::Internal::EXTRA_PREC), prec) if x < 0.5
54
+ return BigDecimal::Internal.underflow_computation_result if x > 5000000000 # erfc(5000000000) < 1e-10000000000000000000 (underflow)
55
+
56
+ if x > 8
57
+ y = erfc_bit_burst(x, prec + BigDecimal::Internal::EXTRA_PREC)
58
+ return y.mult(1, prec) if y
59
+ end
60
+
61
+ # erfc(x) = 1 - erf(x) < exp(-x**2)/x/sqrt(pi)
62
+ # Precision of erf(x) needs about log10(exp(-x**2)/x/sqrt(pi)) extra digits
63
+ log10 = 2.302585092994046
64
+ xf = x.to_f
65
+ high_prec = prec + BigDecimal::Internal::EXTRA_PREC + ((xf**2 + Math.log(xf) + Math.log(Math::PI)/2) / log10).ceil
66
+ BigDecimal(1).sub(erf_bit_burst(x, high_prec), prec)
67
+ end
68
+
69
+ # Matrix multiplication. m1 and m2 are size*size length array that represents size*size matrix
70
+ def self.matrix_mult(m1, m2, size, prec)
71
+ (size * size).times.map do |i|
72
+ size.times.map do |k|
73
+ m1[i / size * size + k].mult(m2[size * k + i % size], prec)
74
+ end.reduce {|a, b| a.add(b, prec) }
75
+ end
76
+ end
77
+
78
+ # Returns (erf(x + a) - erf(a)) * exp(a**2) * sqrt(pi) / 2 calculated with binary splitting method.
79
+ def self.erf_binary_splitting_diff(x, a, prec)
80
+ # Let f(x) = (erf(x + a) - erf(a)) * exp(a**2) * sqrt(pi) / 2
81
+ # f(x) satisfies the following differential equation:
82
+ # 2*(x+a)*f'(x) + f''(x) = 0
83
+ # We can derive the following recurrence for the Taylor coefficients of f:
84
+ # f(x) = x * (c0 + c1*x + c2*x**2 + c3*x**3 + ...)
85
+ # c(0) = 1
86
+ # c(1) = -a
87
+ # c(i) = -2 * (a * c(i - 1) + c(i - 2) * (i - 1) / i) / (i + 1)
88
+
89
+ # Estimate required number of terms by calculating c(i) with low precision
90
+ low_prec = 10
91
+ a_low = a.mult(1, low_prec)
92
+ x_low = x.mult(1, low_prec)
93
+ coefs = [BigDecimal(1), -a_low]
94
+ xn = BigDecimal(1)
95
+ threshold = BigDecimal(1)._decimal_shift(-prec)
96
+ steps = (2..).find do |n|
97
+ prevprev, prev = coefs
98
+ xn = xn.mult(x_low, low_prec)
99
+ coefs = prev, (a_low * prev + (prevprev * (n - 1)).div(n, low_prec)).mult(-2, low_prec).div(n + 1, low_prec)
100
+ coefs[0].mult(xn, low_prec).abs < threshold && coefs[1].mult(xn * x_low, low_prec).abs < threshold
101
+ end
102
+
103
+ # Let M(i) be a 2x2 matrix that generates the next coefficients vector (c(i-1), c(i))
104
+ # from the previous two coefficients (c(i-2), c(i-1)).
105
+ # M(i) = | 0, 1 |
106
+ # | -2*(i-1)/i/(i+1), -2*a/(i+1) |
107
+ #
108
+ # Then, we can calculate (c(steps-1), c(steps)) as M(steps)*M(steps-1)*...*M(2)*Vector(c0, c1).
109
+ #
110
+ # Calculate a matrix that represents the sum of the Taylor series:
111
+ # SumMatrix = ((((...+I)x*M4+I)*x*M3+I)*M2*x+I)
112
+ # Actual sum can be calculated as:
113
+ # SumMatrix * Vector(c0, c1) = Vector(c0+c1*x+c2*x**2+c3*x**3+..., _)
114
+ # In this binary splitting method, adjacent two operations are combined into one repeatedly.
115
+ # ((...) * x * A + B) / C is the form of each operation. A and B are 2x2 matrices, C is a scalar.
116
+
117
+ zero = BigDecimal(0)
118
+ operations = (2..steps + 2).map do |i|
119
+ d = BigDecimal(i * (i + 1))
120
+ [[zero, d, BigDecimal(-2 * (i - 1)), a * (-2 * i)], [d, zero, zero, d], d]
121
+ end
122
+
123
+ while operations.size > 1
124
+ xpow = xpow ? xpow.mult(xpow, prec) : x.mult(1, prec)
125
+ operations = operations.each_slice(2).map do |op1, op2|
126
+ # Combine two operations into one:
127
+ # (((Remaining * x * A2 + B2) / C2) * x * A1 + B1) / C1
128
+ # ((Remaining * (x*x) * (A2*A1) + (x*B2*A1+B1*C2)) / (C1*C2)
129
+ # Therefore, combined operation can be represented as:
130
+ # Anext = A2 * A1
131
+ # Bnext = x * B2 * A1 + B1 * C2
132
+ # Cnext = C1 * C2
133
+ # xnext = x * x
134
+ a1, b1, c1 = op1
135
+ a2, b2, c2 = op2 || [[zero] * 4, [zero] * 4, BigDecimal(1)]
136
+ [
137
+ matrix_mult(a2, a1, 2, prec),
138
+ array_weighted_sum(matrix_mult(b2, a1, 2, prec), xpow, b1, c2, prec),
139
+ c1.mult(c2, prec),
140
+ ]
141
+ end
142
+ end
143
+ _, sum_matrix, denominator = operations.first
144
+ sum = (sum_matrix[0] - a * sum_matrix[1]).div(denominator, prec)
145
+ x.mult(sum, prec)
146
+ end
147
+
148
+ # Calculates erfc(x) using bit-burst algorithm.
149
+ # Returns nil if the asymptotic expansion does not reach the requested precision.
150
+ def self.erfc_bit_burst(x, prec)
151
+ # By bounding the relative error via |d(erfc)/erfc| <= 2*x*|dx| (erfc(x) decays as exp(-x**2)/x),
152
+ # truncate x to the minimum digits sufficient for prec-digit accuracy of the result.
153
+ x = x.mult(1, prec + Math.log10(2 * x.to_f**2).ceil)
154
+ erf_erfc_bit_burst(x, prec, start_digits: 40, mode: :erfc)
155
+ end
156
+
157
+ # Calculates erf(x) using bit-burst algorithm.
158
+ def self.erf_bit_burst(x, prec)
159
+ # By bounding the error via erf'(x) = (2/sqrt(pi)) * exp(-x**2),
160
+ # truncate x to the minimum digits sufficient for prec-digit accuracy of the result.
161
+ x = x.mult(1, [(prec - x.floor**2 / Math.log(10) + Math.log10(x.ceil)).ceil, 10].max)
162
+ erf_erfc_bit_burst(x, prec, start_digits: 8, mode: :erf)
163
+ end
164
+
165
+ # Calculates erf or erfc using bit-burst algorithm.
166
+ # Returns nil if erfc mode cannot reach the requested precision.
167
+ def self.erf_erfc_bit_burst(x, prec, start_digits:, mode:)
168
+ digits = [-x.exponent * 2, start_digits].max
169
+ partial = x.truncate(digits)
170
+ case mode
171
+ when :erf
172
+ f = erf_exp2_binary_splitting(partial, prec)
173
+ when :erfc
174
+ f = erfc_exp2_asymptotic_binary_splitting(partial, prec)
175
+ return unless f
176
+ end
177
+
178
+ exp_scale = BigMath.exp(-partial * partial, prec)
179
+ f = f.mult(exp_scale, prec)
180
+
181
+ calculated_x = partial
182
+ x -= partial
183
+
184
+ until x.zero?
185
+ digits *= 2
186
+ partial = x.truncate(digits)
187
+ next if partial.zero?
188
+
189
+ diff_prec = [prec - f.exponent + exp_scale.exponent + partial.exponent, 1].max
190
+ diff = erf_binary_splitting_diff(partial, calculated_x, diff_prec)
191
+ case mode
192
+ when :erf
193
+ f = f.add(diff.mult(exp_scale, prec), prec)
194
+ when :erfc
195
+ f = f.sub(diff.mult(exp_scale, prec), prec)
196
+ end
197
+
198
+ calculated_x += partial
199
+ x -= partial
200
+ exp_scale = exp_scale.mult(BigMath.exp(partial * (partial - 2 * calculated_x), diff_prec), diff_prec) unless x.zero?
201
+ end
202
+ f.mult(BigDecimal(2).div(BigMath::PI(prec).sqrt(prec), prec), prec)
203
+ end
204
+
205
+ # Matrix/Vector weighted sum
206
+ def self.array_weighted_sum(m1, w1, m2, w2, prec)
207
+ m1.zip(m2).map {|v1, v2| (v1 * w1).add(v2 * w2, prec) }
208
+ end
209
+
210
+ # Calculates Taylor expansion of erf(x)*exp(x**2)*sqrt(pi)/2 with binary splitting method.
211
+ def self.erf_exp2_binary_splitting(x, prec)
212
+ # Let f(x) = erf(x)*exp(x**2)*sqrt(pi)/2
213
+ # = c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...
214
+ # f(x) is designed to make all coefficients positive so that we don't need to consider cancellation error.
215
+ #
216
+ # f(x) satisfies the following differential equation:
217
+ # f'(x) = 1 + 2 * x * f(x)
218
+ # f'(x) = c1 + 2*c2*x + 3*c3*x**2 + 4*c4*x**3 + 5*c5*x**4 + ...
219
+ # = 1+2*x*(c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...)
220
+ # therefore,
221
+ # c0 = 0
222
+ # c1 = 1
223
+ # c2 = 2 * (c0 + c1) / 2
224
+ # c3 = 2 * (c1 + c2) / 3
225
+ # c4 = 2 * (c2 + c3) / 4
226
+
227
+ # Find the smallest n where the n-th Taylor term |c_n * x^n| falls below the precision
228
+ # threshold, using a Stirling-based upper bound on |c_n|.
229
+ log10f = Math.log(10)
230
+ cexponent = Math.log10(Math.sqrt(2)) + BigDecimal::Internal.float_log(x.abs) / log10f
231
+
232
+ x_to_f = x < 1e-300 ? 1e-300 : x.to_f # x.to_f may underflow when x is very small (e.g. 1e-400)
233
+ steps = (2..).bsearch do |n|
234
+ x_to_f ** 2 < n && n * cexponent + Math.lgamma(n / 2)[0] / log10f + n * Math.log10(2) - Math.lgamma(n - 1)[0] / log10f < -prec + x_to_f**2 / log10f
235
+ end
236
+
237
+ denominators = (steps / 2).times.map {|i| 2 * i + 3 }
238
+ x.mult(1 + BigDecimal::Internal.taylor_sum_binary_splitting(2 * x * x, denominators, prec), prec)
239
+ end
240
+
241
+ # Calculates asymptotic expansion of erfc(x)*exp(x**2)*sqrt(pi)/2 with binary splitting method
242
+ def self.erfc_exp2_asymptotic_binary_splitting(x, prec)
243
+ # Let f(x) = erfc(x)*sqrt(pi)*exp(x**2)/2
244
+ # f(x) satisfies the following differential equation:
245
+ # 2*x*f(x) = f'(x) + 1
246
+ # From the above equation, we can derive the following asymptotic expansion:
247
+ # f(x) = (0..kmax).sum { (-1)**k * (2*k)! / 4**k / k! / x**(2*k) } / x / 2
248
+
249
+ # This asymptotic expansion does not converge.
250
+ # But if there is a k that satisfies (2*k)! / 4**k / k! / x**(2*k) < 10**(-prec),
251
+ # It is enough to calculate erfc within the given precision.
252
+ # Using Stirling's approximation, we can simplify this condition to:
253
+ # log(2)/2 + k*log(k) - k - 2*k*log(x) < -prec*log(10)
254
+ # and the left side is minimized when k = x**2.
255
+ xf = x.to_f
256
+ kmax = (1..(xf ** 2).floor).bsearch do |k|
257
+ Math.log(2) / 2 + k * Math.log(k) - k - 2 * k * Math.log(xf) < -prec * Math.log(10)
258
+ end
259
+ return unless kmax
260
+
261
+ # Convert asymptotic expansion to nested form:
262
+ # 1 + a/x + a*b/x/x + a*b*c/x/x/x + a*b*c/x/x/x*rest
263
+ # = 1 + (a/x) * (1 + (b/x) * (1 + (c/x) * (1 + rest)))
264
+ #
265
+ # And calculate it with binary splitting:
266
+ # (a1/d + b1/d * (a2/d + b2/d * (rest)))
267
+ # = ((a1*d+b1*a2)/(d*d) + b1*b2/(d*denominator) * (rest)))
268
+ denominator = x.mult(x, prec).mult(2, prec)
269
+ fractions = (1..kmax).map do |k|
270
+ [denominator, BigDecimal(1 - 2 * k)]
271
+ end
272
+ while fractions.size > 1
273
+ fractions = fractions.each_slice(2).map do |fraction1, fraction2|
274
+ a1, b1 = fraction1
275
+ a2, b2 = fraction2 || [BigDecimal(0), denominator]
276
+ [
277
+ a1.mult(denominator, prec).add(b1.mult(a2, prec), prec),
278
+ b1.mult(b2, prec),
279
+ ]
280
+ end
281
+ denominator = denominator.mult(denominator, prec)
282
+ end
283
+ # Plug rest = 1 into the merged form: the innermost "(1 + rest)" of the nested expansion
284
+ # evaluates to 1 at truncation (rest = 0).
285
+ sum = fractions[0][0].add(fractions[0][1], prec).div(denominator, prec)
286
+ sum.div(x, prec) / 2
287
+ end
288
+ end
289
+
290
+ private_constant :Erf
291
+ end