bigdecimal 3.2.3 → 3.3.0
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/ext/bigdecimal/bigdecimal.c +37 -13
- data/lib/bigdecimal/math.rb +85 -70
- data/lib/bigdecimal/util.rb +15 -14
- data/lib/bigdecimal.rb +67 -51
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bbfee49daa3925fcd286952eb946fdf8d2f266d907f208878b9c61f6ee556557
|
4
|
+
data.tar.gz: d7052a600cc71f060331f14e9a9e36e41251239d53640effa2984221d8ce18d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3850b87c3a249da319665bfbd12a9743f06d844e7315c3d2120ca0c972d5686ba61747cbb9c678ff64dfc94e8c91d7611b58775d5bb6aafe95241e390d802fb
|
7
|
+
data.tar.gz: 9fcb29c1a5abcbb8ab15af42c177f7d55a23695a6c14248718431b73bd019e4e90b51bd038aca6de356e2bf31040e44612b4c2fcbb807d71f2117cef3681a15b
|
data/ext/bigdecimal/bigdecimal.c
CHANGED
@@ -31,7 +31,7 @@
|
|
31
31
|
#include "bits.h"
|
32
32
|
#include "static_assert.h"
|
33
33
|
|
34
|
-
#define BIGDECIMAL_VERSION "3.
|
34
|
+
#define BIGDECIMAL_VERSION "3.3.0"
|
35
35
|
|
36
36
|
/* #define ENABLE_NUMERIC_STRING */
|
37
37
|
|
@@ -252,10 +252,21 @@ static const rb_data_type_t BigDecimal_data_type = {
|
|
252
252
|
#endif
|
253
253
|
};
|
254
254
|
|
255
|
+
// TypedData_Wrap_Struct may fail if there is no memory, or GC.add_stress_to_class(BigDecimal) is set.
|
256
|
+
// We need to first allocate empty struct, allocate Real struct, and then set the data pointer.
|
257
|
+
typedef struct { VALUE _obj; } NULL_WRAPPED_VALUE;
|
258
|
+
static NULL_WRAPPED_VALUE
|
259
|
+
BigDecimal_alloc_empty_struct(VALUE klass)
|
260
|
+
{
|
261
|
+
return (NULL_WRAPPED_VALUE) { TypedData_Wrap_Struct(klass, &BigDecimal_data_type, NULL) };
|
262
|
+
}
|
263
|
+
|
255
264
|
static VALUE
|
256
|
-
BigDecimal_wrap_struct(
|
265
|
+
BigDecimal_wrap_struct(NULL_WRAPPED_VALUE v, Real *real)
|
257
266
|
{
|
258
|
-
VALUE obj =
|
267
|
+
VALUE obj = v._obj;
|
268
|
+
assert(RTYPEDDATA_DATA(obj) == NULL);
|
269
|
+
RTYPEDDATA_DATA(obj) = real;
|
259
270
|
RB_OBJ_FREEZE(obj);
|
260
271
|
return obj;
|
261
272
|
}
|
@@ -265,8 +276,9 @@ MAYBE_UNUSED(static inline BDVALUE rbd_allocate_struct_zero_wrap(int sign, size_
|
|
265
276
|
static BDVALUE
|
266
277
|
rbd_allocate_struct_zero_wrap(int sign, size_t const digits)
|
267
278
|
{
|
279
|
+
NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal);
|
268
280
|
Real *real = rbd_allocate_struct_zero(sign, digits);
|
269
|
-
return (BDVALUE) { BigDecimal_wrap_struct(
|
281
|
+
return (BDVALUE) { BigDecimal_wrap_struct(null_wrapped, real), real };
|
270
282
|
}
|
271
283
|
|
272
284
|
static inline int
|
@@ -1041,9 +1053,10 @@ check_int_precision(VALUE v)
|
|
1041
1053
|
static NULLABLE_BDVALUE
|
1042
1054
|
CreateFromString(const char *str, VALUE klass, bool strict_p, bool raise_exception)
|
1043
1055
|
{
|
1056
|
+
NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(klass);
|
1044
1057
|
Real *pv = VpAlloc(str, strict_p, raise_exception);
|
1045
1058
|
if (!pv) return (NULLABLE_BDVALUE) { Qnil, NULL };
|
1046
|
-
return (NULLABLE_BDVALUE) { BigDecimal_wrap_struct(
|
1059
|
+
return (NULLABLE_BDVALUE) { BigDecimal_wrap_struct(null_wrapped, pv), pv };
|
1047
1060
|
}
|
1048
1061
|
|
1049
1062
|
static Real *
|
@@ -1720,17 +1733,26 @@ BigDecimal_DoDivmod(VALUE self, VALUE r, NULLABLE_BDVALUE *div, NULLABLE_BDVALUE
|
|
1720
1733
|
*mod = (NULLABLE_BDVALUE) { nan, DATA_PTR(nan) };
|
1721
1734
|
goto Done;
|
1722
1735
|
}
|
1723
|
-
if (VpIsInf(b.real)) {
|
1724
|
-
VALUE zero = BigDecimal_positive_zero();
|
1725
|
-
*div = (NULLABLE_BDVALUE) { zero, DATA_PTR(zero) };
|
1726
|
-
*mod = bdvalue_nullable(a);
|
1727
|
-
goto Done;
|
1728
|
-
}
|
1729
1736
|
if (VpIsZero(a.real)) {
|
1730
1737
|
VALUE zero = BigDecimal_positive_zero();
|
1731
1738
|
*div = *mod = (NULLABLE_BDVALUE) { zero, DATA_PTR(zero) };
|
1732
1739
|
goto Done;
|
1733
1740
|
}
|
1741
|
+
if (VpIsInf(b.real)) {
|
1742
|
+
if (!truncate && VpGetSign(a.real) * VpGetSign(b.real) < 0) {
|
1743
|
+
BDVALUE minus_one = NewZeroWrap(1, BASE_FIG);
|
1744
|
+
VpSetOne(minus_one.real);
|
1745
|
+
VpSetSign(minus_one.real, -1);
|
1746
|
+
RB_GC_GUARD(minus_one.bigdecimal);
|
1747
|
+
*div = bdvalue_nullable(minus_one);
|
1748
|
+
*mod = bdvalue_nullable(b);
|
1749
|
+
} else {
|
1750
|
+
VALUE zero = BigDecimal_positive_zero();
|
1751
|
+
*div = (NULLABLE_BDVALUE) { zero, DATA_PTR(zero) };
|
1752
|
+
*mod = bdvalue_nullable(a);
|
1753
|
+
}
|
1754
|
+
goto Done;
|
1755
|
+
}
|
1734
1756
|
|
1735
1757
|
a_exponent = VpExponent10(a.real);
|
1736
1758
|
b_exponent = VpExponent10(b.real);
|
@@ -2580,6 +2602,7 @@ check_exception(VALUE bd)
|
|
2580
2602
|
static VALUE
|
2581
2603
|
rb_uint64_convert_to_BigDecimal(uint64_t uval)
|
2582
2604
|
{
|
2605
|
+
NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal);
|
2583
2606
|
Real *vp;
|
2584
2607
|
if (uval == 0) {
|
2585
2608
|
vp = rbd_allocate_struct(1);
|
@@ -2621,7 +2644,7 @@ rb_uint64_convert_to_BigDecimal(uint64_t uval)
|
|
2621
2644
|
MEMCPY(vp->frac, buf + BIGDECIMAL_INT64_MAX_LENGTH - len, DECDIG, len);
|
2622
2645
|
}
|
2623
2646
|
|
2624
|
-
return BigDecimal_wrap_struct(
|
2647
|
+
return BigDecimal_wrap_struct(null_wrapped, vp);
|
2625
2648
|
}
|
2626
2649
|
|
2627
2650
|
static VALUE
|
@@ -2905,12 +2928,13 @@ rb_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
|
|
2905
2928
|
if (digs == SIZE_MAX)
|
2906
2929
|
return check_exception(val);
|
2907
2930
|
|
2931
|
+
NULL_WRAPPED_VALUE null_wrapped = BigDecimal_alloc_empty_struct(rb_cBigDecimal);
|
2908
2932
|
Real *vp;
|
2909
2933
|
TypedData_Get_Struct(val, Real, &BigDecimal_data_type, vp);
|
2910
2934
|
vp = VpCopy(NULL, vp);
|
2911
2935
|
RB_GC_GUARD(val);
|
2912
2936
|
|
2913
|
-
VALUE copy = BigDecimal_wrap_struct(
|
2937
|
+
VALUE copy = BigDecimal_wrap_struct(null_wrapped, vp);
|
2914
2938
|
/* TODO: rounding */
|
2915
2939
|
return check_exception(copy);
|
2916
2940
|
}
|
data/lib/bigdecimal/math.rb
CHANGED
@@ -7,6 +7,7 @@ require 'bigdecimal'
|
|
7
7
|
# sqrt(x, prec)
|
8
8
|
# sin (x, prec)
|
9
9
|
# cos (x, prec)
|
10
|
+
# tan (x, prec)
|
10
11
|
# atan(x, prec)
|
11
12
|
# PI (prec)
|
12
13
|
# E (prec) == exp(1.0,prec)
|
@@ -24,8 +25,8 @@ require 'bigdecimal'
|
|
24
25
|
#
|
25
26
|
# include BigMath
|
26
27
|
#
|
27
|
-
# a = BigDecimal((PI(
|
28
|
-
# puts sin(a,100) # => 0.
|
28
|
+
# a = BigDecimal((PI(49)/2).to_s)
|
29
|
+
# puts sin(a,100) # => 0.9999999999...9999999986e0
|
29
30
|
#
|
30
31
|
module BigMath
|
31
32
|
module_function
|
@@ -36,13 +37,42 @@ module BigMath
|
|
36
37
|
# Computes the square root of +decimal+ to the specified number of digits of
|
37
38
|
# precision, +numeric+.
|
38
39
|
#
|
39
|
-
# BigMath.sqrt(BigDecimal('2'),
|
40
|
-
# #=> "0.
|
40
|
+
# BigMath.sqrt(BigDecimal('2'), 32).to_s
|
41
|
+
# #=> "0.14142135623730950488016887242097e1"
|
41
42
|
#
|
42
43
|
def sqrt(x, prec)
|
44
|
+
BigDecimal::Internal.validate_prec(prec, :sqrt)
|
45
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sqrt)
|
43
46
|
x.sqrt(prec)
|
44
47
|
end
|
45
48
|
|
49
|
+
|
50
|
+
# Returns [sign, reduced_x] where reduced_x is in -pi/2..pi/2
|
51
|
+
# and satisfies sin(x) = sign * sin(reduced_x)
|
52
|
+
# If add_half_pi is true, adds pi/2 to x before reduction.
|
53
|
+
# Precision of pi is adjusted to ensure reduced_x has the required precision.
|
54
|
+
private_class_method def _sin_periodic_reduction(x, prec, add_half_pi: false) # :nodoc:
|
55
|
+
return [1, x] if -Math::PI/2 <= x && x <= Math::PI/2 && !add_half_pi
|
56
|
+
|
57
|
+
mod_prec = prec + BigDecimal.double_fig
|
58
|
+
pi_extra_prec = [x.exponent, 0].max + BigDecimal.double_fig
|
59
|
+
while true
|
60
|
+
pi = PI(mod_prec + pi_extra_prec)
|
61
|
+
half_pi = pi / 2
|
62
|
+
div, mod = (add_half_pi ? x + pi : x + half_pi).divmod(pi)
|
63
|
+
mod -= half_pi
|
64
|
+
if mod.zero? || mod_prec + mod.exponent <= 0
|
65
|
+
# mod is too small to estimate required pi precision
|
66
|
+
mod_prec = mod_prec * 3 / 2 + BigDecimal.double_fig
|
67
|
+
elsif mod_prec + mod.exponent < prec
|
68
|
+
# Estimate required precision of pi
|
69
|
+
mod_prec = prec - mod.exponent + BigDecimal.double_fig
|
70
|
+
else
|
71
|
+
return [div % 2 == 0 ? 1 : -1, mod.mult(1, prec)]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
46
76
|
# call-seq:
|
47
77
|
# sin(decimal, numeric) -> BigDecimal
|
48
78
|
#
|
@@ -51,42 +81,33 @@ module BigMath
|
|
51
81
|
#
|
52
82
|
# If +decimal+ is Infinity or NaN, returns NaN.
|
53
83
|
#
|
54
|
-
# BigMath.sin(BigMath.PI(5)/4,
|
55
|
-
# #=> "0.
|
84
|
+
# BigMath.sin(BigMath.PI(5)/4, 32).to_s
|
85
|
+
# #=> "0.70710807985947359435812921837984e0"
|
56
86
|
#
|
57
87
|
def sin(x, prec)
|
58
|
-
|
59
|
-
|
88
|
+
BigDecimal::Internal.validate_prec(prec, :sin)
|
89
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sin)
|
90
|
+
return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
|
60
91
|
n = prec + BigDecimal.double_fig
|
61
92
|
one = BigDecimal("1")
|
62
93
|
two = BigDecimal("2")
|
63
|
-
|
64
|
-
if x > 6
|
65
|
-
twopi = two * BigMath.PI(prec + x.exponent)
|
66
|
-
if x > 30
|
67
|
-
x %= twopi
|
68
|
-
else
|
69
|
-
x -= twopi while x > twopi
|
70
|
-
end
|
71
|
-
end
|
94
|
+
sign, x = _sin_periodic_reduction(x, n)
|
72
95
|
x1 = x
|
73
96
|
x2 = x.mult(x,n)
|
74
|
-
sign = 1
|
75
97
|
y = x
|
76
98
|
d = y
|
77
99
|
i = one
|
78
100
|
z = one
|
79
101
|
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
|
80
102
|
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
81
|
-
|
82
|
-
x1 = x2.mult(x1,n)
|
103
|
+
x1 = -x2.mult(x1,n)
|
83
104
|
i += two
|
84
105
|
z *= (i-one) * i
|
85
|
-
d =
|
106
|
+
d = x1.div(z,m)
|
86
107
|
y += d
|
87
108
|
end
|
88
109
|
y = BigDecimal("1") if y > 1
|
89
|
-
|
110
|
+
y.mult(sign, prec)
|
90
111
|
end
|
91
112
|
|
92
113
|
# call-seq:
|
@@ -97,41 +118,34 @@ module BigMath
|
|
97
118
|
#
|
98
119
|
# If +decimal+ is Infinity or NaN, returns NaN.
|
99
120
|
#
|
100
|
-
# BigMath.cos(BigMath.PI(
|
101
|
-
# #=> "-0.
|
121
|
+
# BigMath.cos(BigMath.PI(16), 32).to_s
|
122
|
+
# #=> "-0.99999999999999999999999999999997e0"
|
102
123
|
#
|
103
124
|
def cos(x, prec)
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
x1 = x2.mult(x1,n)
|
129
|
-
i += two
|
130
|
-
z *= (i-one) * i
|
131
|
-
d = sign * x1.div(z,m)
|
132
|
-
y += d
|
133
|
-
end
|
134
|
-
y < -1 ? BigDecimal("-1") : y > 1 ? BigDecimal("1") : y
|
125
|
+
BigDecimal::Internal.validate_prec(prec, :cos)
|
126
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cos)
|
127
|
+
return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
|
128
|
+
sign, x = _sin_periodic_reduction(x, prec + BigDecimal.double_fig, add_half_pi: true)
|
129
|
+
sign * sin(x, prec)
|
130
|
+
end
|
131
|
+
|
132
|
+
# call-seq:
|
133
|
+
# tan(decimal, numeric) -> BigDecimal
|
134
|
+
#
|
135
|
+
# Computes the tangent of +decimal+ to the specified number of digits of
|
136
|
+
# precision, +numeric+.
|
137
|
+
#
|
138
|
+
# If +decimal+ is Infinity or NaN, returns NaN.
|
139
|
+
#
|
140
|
+
# BigMath.tan(BigDecimal("0.0"), 4).to_s
|
141
|
+
# #=> "0.0"
|
142
|
+
#
|
143
|
+
# BigMath.tan(BigMath.PI(24) / 4, 32).to_s
|
144
|
+
# #=> "0.99999999999999999999999830836025e0"
|
145
|
+
#
|
146
|
+
def tan(x, prec)
|
147
|
+
BigDecimal::Internal.validate_prec(prec, :tan)
|
148
|
+
sin(x, prec + BigDecimal.double_fig).div(cos(x, prec + BigDecimal.double_fig), prec)
|
135
149
|
end
|
136
150
|
|
137
151
|
# call-seq:
|
@@ -142,17 +156,18 @@ module BigMath
|
|
142
156
|
#
|
143
157
|
# If +decimal+ is NaN, returns NaN.
|
144
158
|
#
|
145
|
-
# BigMath.atan(BigDecimal('-1'),
|
146
|
-
# #=> "-0.
|
159
|
+
# BigMath.atan(BigDecimal('-1'), 32).to_s
|
160
|
+
# #=> "-0.78539816339744830961566084581988e0"
|
147
161
|
#
|
148
162
|
def atan(x, prec)
|
149
|
-
|
150
|
-
|
151
|
-
|
163
|
+
BigDecimal::Internal.validate_prec(prec, :atan)
|
164
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atan)
|
165
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
166
|
+
n = prec + BigDecimal.double_fig
|
167
|
+
pi = PI(n)
|
152
168
|
x = -x if neg = x < 0
|
153
169
|
return pi.div(neg ? -2 : 2, prec) if x.infinite?
|
154
|
-
return pi
|
155
|
-
n = prec + BigDecimal.double_fig
|
170
|
+
return pi.div(neg ? -4 : 4, prec) if x.round(prec) == 1
|
156
171
|
x = BigDecimal("1").div(x, n) if inv = x > 1
|
157
172
|
x = (-1 + sqrt(1 + x.mult(x, n), n)).div(x, n) if dbl = x > 0.5
|
158
173
|
y = x
|
@@ -170,7 +185,7 @@ module BigMath
|
|
170
185
|
y *= 2 if dbl
|
171
186
|
y = pi / 2 - y if inv
|
172
187
|
y = -y if neg
|
173
|
-
y
|
188
|
+
y.mult(1, prec)
|
174
189
|
end
|
175
190
|
|
176
191
|
# call-seq:
|
@@ -179,11 +194,11 @@ module BigMath
|
|
179
194
|
# Computes the value of pi to the specified number of digits of precision,
|
180
195
|
# +numeric+.
|
181
196
|
#
|
182
|
-
# BigMath.PI(
|
183
|
-
# #=> "0.
|
197
|
+
# BigMath.PI(32).to_s
|
198
|
+
# #=> "0.31415926535897932384626433832795e1"
|
184
199
|
#
|
185
200
|
def PI(prec)
|
186
|
-
|
201
|
+
BigDecimal::Internal.validate_prec(prec, :PI)
|
187
202
|
n = prec + BigDecimal.double_fig
|
188
203
|
zero = BigDecimal("0")
|
189
204
|
one = BigDecimal("1")
|
@@ -215,7 +230,7 @@ module BigMath
|
|
215
230
|
pi = pi + d
|
216
231
|
k = k+two
|
217
232
|
end
|
218
|
-
pi
|
233
|
+
pi.mult(1, prec)
|
219
234
|
end
|
220
235
|
|
221
236
|
# call-seq:
|
@@ -224,11 +239,11 @@ module BigMath
|
|
224
239
|
# Computes e (the base of natural logarithms) to the specified number of
|
225
240
|
# digits of precision, +numeric+.
|
226
241
|
#
|
227
|
-
# BigMath.E(
|
228
|
-
# #=> "0.
|
242
|
+
# BigMath.E(32).to_s
|
243
|
+
# #=> "0.27182818284590452353602874713527e1"
|
229
244
|
#
|
230
245
|
def E(prec)
|
231
|
-
|
246
|
+
BigDecimal::Internal.validate_prec(prec, :E)
|
232
247
|
BigMath.exp(1, prec)
|
233
248
|
end
|
234
249
|
end
|
data/lib/bigdecimal/util.rb
CHANGED
@@ -119,8 +119,11 @@ class Rational < Numeric
|
|
119
119
|
#
|
120
120
|
# Returns the value as a BigDecimal.
|
121
121
|
#
|
122
|
-
# The
|
123
|
-
# significant digits for the result.
|
122
|
+
# The +precision+ parameter is used to determine the number of
|
123
|
+
# significant digits for the result. When +precision+ is set to +0+,
|
124
|
+
# the number of digits to represent the float being converted is determined
|
125
|
+
# automatically.
|
126
|
+
# The default +precision+ is +0+.
|
124
127
|
#
|
125
128
|
# require 'bigdecimal'
|
126
129
|
# require 'bigdecimal/util'
|
@@ -129,7 +132,7 @@ class Rational < Numeric
|
|
129
132
|
#
|
130
133
|
# See also Kernel.BigDecimal.
|
131
134
|
#
|
132
|
-
def to_d(precision)
|
135
|
+
def to_d(precision=0)
|
133
136
|
BigDecimal(self, precision)
|
134
137
|
end
|
135
138
|
end
|
@@ -141,29 +144,27 @@ class Complex < Numeric
|
|
141
144
|
# cmp.to_d(precision) -> bigdecimal
|
142
145
|
#
|
143
146
|
# Returns the value as a BigDecimal.
|
147
|
+
# If the imaginary part is not +0+, an error is raised
|
144
148
|
#
|
145
|
-
# The +precision+ parameter is
|
146
|
-
#
|
147
|
-
#
|
149
|
+
# The +precision+ parameter is used to determine the number of
|
150
|
+
# significant digits for the result. When +precision+ is set to +0+,
|
151
|
+
# the number of digits to represent the float being converted is determined
|
152
|
+
# automatically.
|
153
|
+
# The default +precision+ is +0+.
|
148
154
|
#
|
149
155
|
# require 'bigdecimal'
|
150
156
|
# require 'bigdecimal/util'
|
151
157
|
#
|
152
158
|
# Complex(0.1234567, 0).to_d(4) # => 0.1235e0
|
153
159
|
# Complex(Rational(22, 7), 0).to_d(3) # => 0.314e1
|
160
|
+
# Complex(1, 1).to_d # raises ArgumentError
|
154
161
|
#
|
155
162
|
# See also Kernel.BigDecimal.
|
156
163
|
#
|
157
|
-
def to_d(
|
164
|
+
def to_d(precision=0)
|
158
165
|
BigDecimal(self) unless self.imag.zero? # to raise error
|
159
166
|
|
160
|
-
|
161
|
-
case self.real
|
162
|
-
when Rational
|
163
|
-
BigDecimal(self.real) # to raise error
|
164
|
-
end
|
165
|
-
end
|
166
|
-
self.real.to_d(*args)
|
167
|
+
BigDecimal(self.real, precision)
|
167
168
|
end
|
168
169
|
end
|
169
170
|
|
data/lib/bigdecimal.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
if RUBY_ENGINE == 'jruby'
|
2
2
|
JRuby::Util.load_ext("org.jruby.ext.bigdecimal.BigDecimalLibrary")
|
3
|
-
|
3
|
+
|
4
|
+
class BigDecimal
|
5
|
+
def _decimal_shift(i) # :nodoc:
|
6
|
+
to_java.move_point_right(i).to_d
|
7
|
+
end
|
8
|
+
end
|
4
9
|
else
|
5
10
|
require 'bigdecimal.so'
|
6
11
|
end
|
@@ -15,7 +20,7 @@ class BigDecimal
|
|
15
20
|
when BigDecimal
|
16
21
|
return x
|
17
22
|
when Integer, Float
|
18
|
-
return BigDecimal(x)
|
23
|
+
return BigDecimal(x, 0)
|
19
24
|
when Rational
|
20
25
|
return BigDecimal(x, [prec, 2 * BigDecimal.double_fig].max)
|
21
26
|
end
|
@@ -160,22 +165,32 @@ class BigDecimal
|
|
160
165
|
return BigDecimal(1).div(inv, prec)
|
161
166
|
end
|
162
167
|
|
163
|
-
int_part = y.fix.to_i
|
164
168
|
prec2 = prec + BigDecimal.double_fig
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
169
|
+
|
170
|
+
if frac_part.zero? && y.exponent < Math.log(prec) * 5 + 20
|
171
|
+
# Use exponentiation by squaring if y is an integer and not too large
|
172
|
+
pow_prec = prec2 + y.exponent
|
173
|
+
n = 1
|
174
|
+
xn = x
|
175
|
+
ans = BigDecimal(1)
|
176
|
+
int_part = y.fix.to_i
|
177
|
+
while true
|
178
|
+
ans = ans.mult(xn, pow_prec) if int_part.allbits?(n)
|
179
|
+
n <<= 1
|
180
|
+
break if n > int_part
|
181
|
+
xn = xn.mult(xn, pow_prec)
|
182
|
+
end
|
183
|
+
ans.mult(1, prec)
|
184
|
+
else
|
185
|
+
if x > 1
|
186
|
+
# To calculate exp(z, prec), z needs prec+max(z.exponent, 0) precision if z > 0.
|
187
|
+
# Estimate (y*log(x)).exponent
|
188
|
+
logx_exponent = x < 2 ? (x - 1).exponent : Math.log10(x.exponent).round
|
189
|
+
ylogx_exponent = y.exponent + logx_exponent
|
190
|
+
prec2 += [ylogx_exponent, 0].max
|
191
|
+
end
|
192
|
+
BigMath.exp(BigMath.log(x, prec2).mult(y, prec2), prec)
|
177
193
|
end
|
178
|
-
ans.mult(1, prec)
|
179
194
|
end
|
180
195
|
|
181
196
|
# Returns the square root of the value.
|
@@ -190,23 +205,19 @@ class BigDecimal
|
|
190
205
|
raise FloatDomainError, "sqrt of 'NaN'(Not a Number)" if nan?
|
191
206
|
return self if zero?
|
192
207
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
# This feature maybe problematic for some cases.
|
197
|
-
n_digits = n_significant_digits
|
198
|
-
prec = [prec, n_digits].max
|
208
|
+
if prec == 0
|
209
|
+
prec = BigDecimal.limit.nonzero? || n_significant_digits + BigDecimal.double_fig
|
210
|
+
end
|
199
211
|
|
200
212
|
ex = exponent / 2
|
201
213
|
x = _decimal_shift(-2 * ex)
|
202
|
-
y = BigDecimal(Math.sqrt(x.to_f))
|
214
|
+
y = BigDecimal(Math.sqrt(x.to_f), 0)
|
203
215
|
precs = [prec + BigDecimal.double_fig]
|
204
216
|
precs << 2 + precs.last / 2 while precs.last > BigDecimal.double_fig
|
205
217
|
precs.reverse_each do |p|
|
206
218
|
y = y.add(x.div(y, p), p).div(2, p)
|
207
219
|
end
|
208
|
-
y
|
209
|
-
y._decimal_shift(ex)
|
220
|
+
y._decimal_shift(ex).mult(1, prec)
|
210
221
|
end
|
211
222
|
end
|
212
223
|
|
@@ -232,56 +243,67 @@ module BigMath
|
|
232
243
|
|
233
244
|
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log)
|
234
245
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
235
|
-
raise Math::DomainError, '
|
246
|
+
raise Math::DomainError, 'Negative argument for log' if x < 0
|
247
|
+
return -BigDecimal::Internal.infinity_computation_result if x.zero?
|
236
248
|
return BigDecimal::Internal.infinity_computation_result if x.infinite?
|
237
249
|
return BigDecimal(0) if x == 1
|
238
250
|
|
251
|
+
prec2 = prec + BigDecimal.double_fig
|
239
252
|
BigDecimal.save_limit do
|
240
253
|
BigDecimal.limit(0)
|
241
254
|
if x > 10 || x < 0.1
|
242
|
-
log10 = log(BigDecimal(10),
|
255
|
+
log10 = log(BigDecimal(10), prec2)
|
243
256
|
exponent = x.exponent
|
244
257
|
x = x._decimal_shift(-exponent)
|
245
258
|
if x < 0.3
|
246
259
|
x *= 10
|
247
260
|
exponent -= 1
|
248
261
|
end
|
249
|
-
return log10 * exponent
|
262
|
+
return (log10 * exponent).add(log(x, prec2), prec)
|
250
263
|
end
|
251
264
|
|
252
265
|
x_minus_one_exponent = (x - 1).exponent
|
253
|
-
prec += BigDecimal.double_fig
|
254
266
|
|
255
267
|
# log(x) = log(sqrt(sqrt(sqrt(sqrt(x))))) * 2**sqrt_steps
|
256
|
-
sqrt_steps = [Integer.sqrt(
|
268
|
+
sqrt_steps = [Integer.sqrt(prec2) + 3 * x_minus_one_exponent, 0].max
|
257
269
|
|
258
270
|
lg2 = 0.3010299956639812
|
259
|
-
|
271
|
+
sqrt_prec = prec2 + [-x_minus_one_exponent, 0].max + (sqrt_steps * lg2).ceil
|
260
272
|
|
261
273
|
sqrt_steps.times do
|
262
|
-
x = x.sqrt(
|
263
|
-
|
264
|
-
# Workaround for https://github.com/ruby/bigdecimal/issues/354
|
265
|
-
x = x.mult(1, prec2 + BigDecimal.double_fig)
|
274
|
+
x = x.sqrt(sqrt_prec)
|
266
275
|
end
|
267
276
|
|
268
277
|
# Taylor series for log(x) around 1
|
269
278
|
# log(x) = -log((1 + X) / (1 - X)) where X = (x - 1) / (x + 1)
|
270
279
|
# log(x) = 2 * (X + X**3 / 3 + X**5 / 5 + X**7 / 7 + ...)
|
271
|
-
x = (x - 1).div(x + 1,
|
280
|
+
x = (x - 1).div(x + 1, sqrt_prec)
|
272
281
|
y = x
|
273
|
-
x2 = x.mult(x,
|
282
|
+
x2 = x.mult(x, prec2)
|
274
283
|
1.step do |i|
|
275
|
-
n =
|
284
|
+
n = prec2 + x.exponent - y.exponent + x2.exponent
|
276
285
|
break if n <= 0 || x.zero?
|
277
286
|
x = x.mult(x2.round(n - x2.exponent), n)
|
278
|
-
y = y.add(x.div(2 * i + 1, n),
|
287
|
+
y = y.add(x.div(2 * i + 1, n), prec2)
|
279
288
|
end
|
280
289
|
|
281
290
|
y.mult(2 ** (sqrt_steps + 1), prec)
|
282
291
|
end
|
283
292
|
end
|
284
293
|
|
294
|
+
# Taylor series for exp(x) around 0
|
295
|
+
private_class_method def self._exp_taylor(x, prec) # :nodoc:
|
296
|
+
xn = BigDecimal(1)
|
297
|
+
y = BigDecimal(1)
|
298
|
+
1.step do |i|
|
299
|
+
n = prec + xn.exponent
|
300
|
+
break if n <= 0 || xn.zero?
|
301
|
+
xn = xn.mult(x, n).div(i, n)
|
302
|
+
y = y.add(xn, prec)
|
303
|
+
end
|
304
|
+
y
|
305
|
+
end
|
306
|
+
|
285
307
|
# call-seq:
|
286
308
|
# BigMath.exp(decimal, numeric) -> BigDecimal
|
287
309
|
#
|
@@ -298,23 +320,17 @@ module BigMath
|
|
298
320
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
299
321
|
return x.positive? ? BigDecimal::Internal.infinity_computation_result : BigDecimal(0) if x.infinite?
|
300
322
|
return BigDecimal(1) if x.zero?
|
301
|
-
return BigDecimal(1).div(exp(-x, prec), prec) if x < 0
|
302
323
|
|
303
324
|
# exp(x * 10**cnt) = exp(x)**(10**cnt)
|
304
|
-
cnt = x > 1 ? x.exponent : 0
|
325
|
+
cnt = x < -1 || x > 1 ? x.exponent : 0
|
305
326
|
prec2 = prec + BigDecimal.double_fig + cnt
|
306
327
|
x = x._decimal_shift(-cnt)
|
307
|
-
xn = BigDecimal(1)
|
308
|
-
y = BigDecimal(1)
|
309
328
|
|
310
|
-
#
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
xn = xn.mult(x, n).div(i, n)
|
316
|
-
y = y.add(xn, prec2)
|
317
|
-
end
|
329
|
+
# Calculation of exp(small_prec) is fast because calculation of x**n is fast
|
330
|
+
# Calculation of exp(small_abs) converges fast.
|
331
|
+
# exp(x) = exp(small_prec_part + small_abs_part) = exp(small_prec_part) * exp(small_abs_part)
|
332
|
+
x_small_prec = x.round(Integer.sqrt(prec2))
|
333
|
+
y = _exp_taylor(x_small_prec, prec2).mult(_exp_taylor(x.sub(x_small_prec, prec2), prec2), prec2)
|
318
334
|
|
319
335
|
# calculate exp(x * 10**cnt) from exp(x)
|
320
336
|
# exp(x * 10**k) = exp(x * 10**(k - 1)) ** 10
|