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 +4 -4
- data/bigdecimal.gemspec +2 -0
- data/ext/bigdecimal/bigdecimal.c +26 -10
- data/ext/bigdecimal/bigdecimal.h +5 -5
- data/ext/bigdecimal/div.h +61 -34
- data/lib/bigdecimal/math/erf.rb +291 -0
- data/lib/bigdecimal/math/gamma.rb +513 -0
- data/lib/bigdecimal/math.rb +8 -219
- data/sig/big_decimal.rbs +3 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1e3b015d4eafc3c72fda5a95b949fb4413a8fa068cbdad6b4ab9c8f18244dba6
|
|
4
|
+
data.tar.gz: d6f6639c3560978f79e43d3943881e9ad5fb01a9b03cf98c0c056f8f18614cc1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b1ce1badb6a615c8a5576e2e5f5a1861dc3ff9e97b0a37b8788470c47e3f2b18329edabcf904e5ad8215ef8b0805a3d02220ed5cc7c856ec9da7231913b2bc80
|
|
7
|
+
data.tar.gz: 11d06bb5706fc5034cb012f49479095a5d17eabcec8c011956010e13032f228a29b9421abd1fa3f56b7db83b41dc43ecf54bb7b0d60353508c396c169c27ebee
|
data/bigdecimal.gemspec
CHANGED
data/ext/bigdecimal/bigdecimal.c
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
#include "div.h"
|
|
34
34
|
#include "static_assert.h"
|
|
35
35
|
|
|
36
|
-
#define BIGDECIMAL_VERSION "4.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 =
|
|
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
|
-
|
|
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
|
-
|
|
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++) {
|
data/ext/bigdecimal/bigdecimal.h
CHANGED
|
@@ -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
|
|
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
|
-
|
|
13
|
-
|
|
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) +
|
|
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(
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
139
|
-
Real *c = args
|
|
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(
|
|
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
|
-
|
|
178
|
-
|
|
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
|
-
|
|
188
|
-
|
|
214
|
+
struct vp_divd_newton_args args = {c, r, a, b};
|
|
215
|
+
struct vp_settings saved = {VpGetPrecLimit(), VpGetRoundMode()};
|
|
189
216
|
VpSetPrecLimit(0);
|
|
190
|
-
|
|
191
|
-
rb_ensure(VpDivdNewtonInner, (VALUE)args,
|
|
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
|