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 +4 -4
- data/bigdecimal.gemspec +2 -0
- data/ext/bigdecimal/bigdecimal.c +60 -18
- data/ext/bigdecimal/bigdecimal.h +5 -5
- data/ext/bigdecimal/div.h +70 -43
- data/ext/bigdecimal/missing/dtoa.c +184 -137
- data/ext/bigdecimal/ntt.h +5 -5
- data/lib/bigdecimal/math/erf.rb +291 -0
- data/lib/bigdecimal/math/gamma.rb +513 -0
- data/lib/bigdecimal/math.rb +10 -221
- data/lib/bigdecimal.rb +20 -12
- 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
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -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
|
-
|
|
5400
|
-
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
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
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
|
-
|
|
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++) {
|
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,
|
|
@@ -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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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;
|
|
@@ -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(
|
|
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
|
-
|
|
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
|
}
|