decimal 0.0.2 → 0.0.90.pre
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.
- data/COPYING +2 -2
- data/INSTALL +1 -2
- data/README +57 -1
- data/README.1st +1 -0
- data/TODO +25 -0
- data/decimal.c +927 -446
- data/decimal.gemspec +9 -5
- data/depend +1 -1
- data/extconf.rb +14 -0
- data/inum18.h +82 -8
- data/inum191.h +350 -0
- data/inum192.h +332 -0
- data/lib/decimal.rb +2 -0
- data/lib/decimal/math.rb +566 -0
- data/ruby18compat.h +55 -0
- data/test_decimal.rb +546 -0
- metadata +37 -11
data/inum192.h
ADDED
@@ -0,0 +1,332 @@
|
|
1
|
+
/*
|
2
|
+
* Ruby's Integer part from ruby_1_9_2, r28085.
|
3
|
+
*
|
4
|
+
* These are hand copies (with few modifications) taken from original
|
5
|
+
* Ruby's code in "numeric.c" and "bignum.c," so the copyrights are
|
6
|
+
* held by matz and other contributors:
|
7
|
+
*
|
8
|
+
* Copyright (C) 1993-2010 Yukihiro Matsumoto
|
9
|
+
*
|
10
|
+
*/
|
11
|
+
|
12
|
+
/*
|
13
|
+
* copied from bignum.c
|
14
|
+
*/
|
15
|
+
|
16
|
+
#define BDIGITS(x) (RBIGNUM_DIGITS(x))
|
17
|
+
|
18
|
+
#ifndef HAVE_RB_BIGZERO_P
|
19
|
+
#error Ruby 1.9.2 should have rb_bigzero_p()!
|
20
|
+
#endif
|
21
|
+
|
22
|
+
static VALUE
|
23
|
+
rb_big_uminus(VALUE x)
|
24
|
+
{
|
25
|
+
VALUE z = rb_big_clone(x);
|
26
|
+
|
27
|
+
RBIGNUM_SET_SIGN(z, !RBIGNUM_SIGN(x));
|
28
|
+
|
29
|
+
return rb_big_norm(z); /* modified to use exported one */
|
30
|
+
}
|
31
|
+
|
32
|
+
static VALUE
|
33
|
+
rb_big_hash(VALUE x)
|
34
|
+
{
|
35
|
+
st_index_t hash;
|
36
|
+
|
37
|
+
hash = rb_memhash(BDIGITS(x), sizeof(BDIGIT)*RBIGNUM_LEN(x)) ^ RBIGNUM_SIGN(x);
|
38
|
+
return INT2FIX(hash);
|
39
|
+
}
|
40
|
+
|
41
|
+
static VALUE
|
42
|
+
rb_big_odd_p(VALUE num)
|
43
|
+
{
|
44
|
+
if (BDIGITS(num)[0] & 1) {
|
45
|
+
return Qtrue;
|
46
|
+
}
|
47
|
+
return Qfalse;
|
48
|
+
}
|
49
|
+
|
50
|
+
/*
|
51
|
+
* copied from numeric.c
|
52
|
+
*/
|
53
|
+
|
54
|
+
static VALUE
|
55
|
+
flo_to_s(VALUE flt)
|
56
|
+
{
|
57
|
+
char buf[32];
|
58
|
+
double value = RFLOAT_VALUE(flt);
|
59
|
+
char *p, *e;
|
60
|
+
|
61
|
+
if (isinf(value))
|
62
|
+
return rb_usascii_str_new2(value < 0 ? "-Infinity" : "Infinity");
|
63
|
+
else if(isnan(value))
|
64
|
+
return rb_usascii_str_new2("NaN");
|
65
|
+
|
66
|
+
snprintf(buf, sizeof(buf), "%#.15g", value); /* ensure to print decimal point */
|
67
|
+
if (!(e = strchr(buf, 'e'))) {
|
68
|
+
e = buf + strlen(buf);
|
69
|
+
}
|
70
|
+
if (!ISDIGIT(e[-1])) { /* reformat if ended with decimal point (ex 111111111111111.) */
|
71
|
+
snprintf(buf, sizeof(buf), "%#.14e", value);
|
72
|
+
if (!(e = strchr(buf, 'e'))) {
|
73
|
+
e = buf + strlen(buf);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
p = e;
|
77
|
+
while (p[-1]=='0' && ISDIGIT(p[-2]))
|
78
|
+
p--;
|
79
|
+
memmove(p, e, strlen(e)+1);
|
80
|
+
return rb_usascii_str_new2(buf);
|
81
|
+
}
|
82
|
+
|
83
|
+
static VALUE
|
84
|
+
fix_plus(VALUE x, VALUE y)
|
85
|
+
{
|
86
|
+
if (FIXNUM_P(y)) {
|
87
|
+
long a, b, c;
|
88
|
+
VALUE r;
|
89
|
+
|
90
|
+
a = FIX2LONG(x);
|
91
|
+
b = FIX2LONG(y);
|
92
|
+
c = a + b;
|
93
|
+
r = LONG2NUM(c);
|
94
|
+
|
95
|
+
return r;
|
96
|
+
}
|
97
|
+
return rb_big_plus(y, x); /* modified */
|
98
|
+
}
|
99
|
+
|
100
|
+
static VALUE
|
101
|
+
fix_minus(VALUE x, VALUE y)
|
102
|
+
{
|
103
|
+
if (FIXNUM_P(y)) {
|
104
|
+
long a, b, c;
|
105
|
+
VALUE r;
|
106
|
+
|
107
|
+
a = FIX2LONG(x);
|
108
|
+
b = FIX2LONG(y);
|
109
|
+
c = a - b;
|
110
|
+
r = LONG2NUM(c);
|
111
|
+
|
112
|
+
return r;
|
113
|
+
}
|
114
|
+
/* modified */
|
115
|
+
x = rb_int2big(FIX2LONG(x));
|
116
|
+
return rb_big_minus(x, y);
|
117
|
+
}
|
118
|
+
|
119
|
+
#define SQRT_LONG_MAX ((SIGNED_VALUE)1<<((SIZEOF_LONG*CHAR_BIT-1)/2))
|
120
|
+
/*tests if N*N would overflow*/
|
121
|
+
#define FIT_SQRT_LONG(n) (((n)<SQRT_LONG_MAX)&&((n)>=-SQRT_LONG_MAX))
|
122
|
+
|
123
|
+
static VALUE
|
124
|
+
fix_mul(VALUE x, VALUE y)
|
125
|
+
{
|
126
|
+
if (FIXNUM_P(y)) {
|
127
|
+
#ifdef __HP_cc
|
128
|
+
/* avoids an optimization bug of HP aC++/ANSI C B3910B A.06.05 [Jul 25 2005] */
|
129
|
+
volatile
|
130
|
+
#endif
|
131
|
+
long a, b;
|
132
|
+
#if SIZEOF_LONG * 2 <= SIZEOF_LONG_LONG
|
133
|
+
LONG_LONG d;
|
134
|
+
#else
|
135
|
+
long c;
|
136
|
+
VALUE r;
|
137
|
+
#endif
|
138
|
+
|
139
|
+
a = FIX2LONG(x);
|
140
|
+
b = FIX2LONG(y);
|
141
|
+
|
142
|
+
#if SIZEOF_LONG * 2 <= SIZEOF_LONG_LONG
|
143
|
+
d = (LONG_LONG)a * b;
|
144
|
+
if (FIXABLE(d)) return LONG2FIX(d);
|
145
|
+
return rb_ll2inum(d);
|
146
|
+
#else
|
147
|
+
if (FIT_SQRT_LONG(a) && FIT_SQRT_LONG(b))
|
148
|
+
return LONG2FIX(a*b);
|
149
|
+
c = a * b;
|
150
|
+
r = LONG2FIX(c);
|
151
|
+
|
152
|
+
if (a == 0) return x;
|
153
|
+
if (FIX2LONG(r) != c || c/a != b) {
|
154
|
+
r = rb_big_mul(rb_int2big(a), rb_int2big(b));
|
155
|
+
}
|
156
|
+
return r;
|
157
|
+
#endif
|
158
|
+
}
|
159
|
+
/* modified */
|
160
|
+
return rb_big_mul(y, x);
|
161
|
+
}
|
162
|
+
|
163
|
+
static void
|
164
|
+
fixdivmod(long x, long y, long *divp, long *modp)
|
165
|
+
{
|
166
|
+
long div, mod;
|
167
|
+
|
168
|
+
if (y == 0) rb_bug("fixdivmod(): not reached"); /* modified */
|
169
|
+
if (y < 0) {
|
170
|
+
if (x < 0)
|
171
|
+
div = -x / -y;
|
172
|
+
else
|
173
|
+
div = - (x / -y);
|
174
|
+
}
|
175
|
+
else {
|
176
|
+
if (x < 0)
|
177
|
+
div = - (-x / y);
|
178
|
+
else
|
179
|
+
div = x / y;
|
180
|
+
}
|
181
|
+
mod = x - div*y;
|
182
|
+
if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) {
|
183
|
+
mod += y;
|
184
|
+
div -= 1;
|
185
|
+
}
|
186
|
+
if (divp) *divp = div;
|
187
|
+
if (modp) *modp = mod;
|
188
|
+
}
|
189
|
+
|
190
|
+
/* extracted from fix_divide() */
|
191
|
+
static VALUE
|
192
|
+
fix_div(VALUE x, VALUE y)
|
193
|
+
{
|
194
|
+
if (FIXNUM_P(y)) {
|
195
|
+
long div;
|
196
|
+
|
197
|
+
fixdivmod(FIX2LONG(x), FIX2LONG(y), &div, 0);
|
198
|
+
return LONG2NUM(div);
|
199
|
+
}
|
200
|
+
/* modified */
|
201
|
+
x = rb_int2big(FIX2LONG(x));
|
202
|
+
return rb_big_div(x, y);
|
203
|
+
}
|
204
|
+
|
205
|
+
static VALUE
|
206
|
+
fix_divmod(VALUE x, VALUE y)
|
207
|
+
{
|
208
|
+
if (FIXNUM_P(y)) {
|
209
|
+
long div, mod;
|
210
|
+
|
211
|
+
fixdivmod(FIX2LONG(x), FIX2LONG(y), &div, &mod);
|
212
|
+
|
213
|
+
return rb_assoc_new(LONG2NUM(div), LONG2NUM(mod));
|
214
|
+
}
|
215
|
+
/* modified */
|
216
|
+
x = rb_int2big(FIX2LONG(x));
|
217
|
+
return rb_big_divmod(x, y);
|
218
|
+
}
|
219
|
+
|
220
|
+
static VALUE
|
221
|
+
int_pow(long x, unsigned long y)
|
222
|
+
{
|
223
|
+
int neg = x < 0;
|
224
|
+
long z = 1;
|
225
|
+
|
226
|
+
if (neg) x = -x;
|
227
|
+
if (y & 1)
|
228
|
+
z = x;
|
229
|
+
else
|
230
|
+
neg = 0;
|
231
|
+
y &= ~1;
|
232
|
+
do {
|
233
|
+
while (y % 2 == 0) {
|
234
|
+
if (!FIT_SQRT_LONG(x)) {
|
235
|
+
VALUE v;
|
236
|
+
bignum:
|
237
|
+
v = rb_big_pow(rb_int2big(x), LONG2NUM(y));
|
238
|
+
if (z != 1) v = rb_big_mul(rb_int2big(neg ? -z : z), v);
|
239
|
+
return v;
|
240
|
+
}
|
241
|
+
x = x * x;
|
242
|
+
y >>= 1;
|
243
|
+
}
|
244
|
+
{
|
245
|
+
long xz = x * z;
|
246
|
+
if (!POSFIXABLE(xz) || xz / x != z) {
|
247
|
+
goto bignum;
|
248
|
+
}
|
249
|
+
z = xz;
|
250
|
+
}
|
251
|
+
} while (--y);
|
252
|
+
if (neg) z = -z;
|
253
|
+
return LONG2NUM(z);
|
254
|
+
}
|
255
|
+
|
256
|
+
static VALUE fix_odd_p(VALUE num);
|
257
|
+
|
258
|
+
static VALUE
|
259
|
+
fix_pow(VALUE x, VALUE y)
|
260
|
+
{
|
261
|
+
/* static const double zero = 0.0; */
|
262
|
+
long a = FIX2LONG(x);
|
263
|
+
|
264
|
+
if (FIXNUM_P(y)) {
|
265
|
+
long b = FIX2LONG(y);
|
266
|
+
|
267
|
+
if (b < 0)
|
268
|
+
return rb_funcall(rb_rational_raw1(x), rb_intern("**"), 1, y);
|
269
|
+
|
270
|
+
if (b == 0) return INT2FIX(1);
|
271
|
+
if (b == 1) return x;
|
272
|
+
if (a == 0) {
|
273
|
+
if (b > 0) return INT2FIX(0);
|
274
|
+
/* modified */
|
275
|
+
rb_bug("fix_pow(): infinity returned");
|
276
|
+
return Qnil;
|
277
|
+
}
|
278
|
+
if (a == 1) return INT2FIX(1);
|
279
|
+
if (a == -1) {
|
280
|
+
if (b % 2 == 0)
|
281
|
+
return INT2FIX(1);
|
282
|
+
else
|
283
|
+
return INT2FIX(-1);
|
284
|
+
}
|
285
|
+
return int_pow(a, b);
|
286
|
+
}
|
287
|
+
/* modified */
|
288
|
+
if (rb_funcall(y, '<', 1, INT2FIX(0)))
|
289
|
+
return rb_funcall(rb_rational_raw1(x), rb_intern("**"), 1, y);
|
290
|
+
|
291
|
+
if (a == 0) return INT2FIX(0);
|
292
|
+
if (a == 1) return INT2FIX(1);
|
293
|
+
if (a == -1) {
|
294
|
+
/* modified */
|
295
|
+
#define int_even_p(x) \
|
296
|
+
(FIXNUM_P(x) ? !fix_odd_p(x) : !rb_big_odd_p(x))
|
297
|
+
if (int_even_p(y)) return INT2FIX(1);
|
298
|
+
#undef int_even_p
|
299
|
+
else return INT2FIX(-1);
|
300
|
+
}
|
301
|
+
x = rb_int2big(FIX2LONG(x));
|
302
|
+
return rb_big_pow(x, y);
|
303
|
+
}
|
304
|
+
|
305
|
+
static VALUE
|
306
|
+
fix_equal(VALUE x, VALUE y)
|
307
|
+
{
|
308
|
+
if (x == y) return Qtrue;
|
309
|
+
if (FIXNUM_P(y)) return Qfalse;
|
310
|
+
return rb_big_eq(y, x); /* modified */
|
311
|
+
}
|
312
|
+
|
313
|
+
static VALUE
|
314
|
+
fix_cmp(VALUE x, VALUE y)
|
315
|
+
{
|
316
|
+
if (x == y) return INT2FIX(0);
|
317
|
+
if (FIXNUM_P(y)) {
|
318
|
+
if (FIX2LONG(x) > FIX2LONG(y)) return INT2FIX(1);
|
319
|
+
return INT2FIX(-1);
|
320
|
+
}
|
321
|
+
/* modified */
|
322
|
+
return rb_big_cmp(rb_int2big(FIX2LONG(x)), y);
|
323
|
+
}
|
324
|
+
|
325
|
+
static VALUE
|
326
|
+
fix_odd_p(VALUE num)
|
327
|
+
{
|
328
|
+
if (num & 2) {
|
329
|
+
return Qtrue;
|
330
|
+
}
|
331
|
+
return Qfalse;
|
332
|
+
}
|
data/lib/decimal.rb
ADDED
data/lib/decimal/math.rb
ADDED
@@ -0,0 +1,566 @@
|
|
1
|
+
module Decimal::Math
|
2
|
+
module_function
|
3
|
+
|
4
|
+
# algorithm of functions below are from book: ISBN4-87408-414-1
|
5
|
+
# sqrt, cbrt, erf, erfc, *p_gamma, *q_gamma, *bernoulli
|
6
|
+
|
7
|
+
# functions and variables that prefixed by "_decimal_internal" are
|
8
|
+
# indended to internal use only
|
9
|
+
def _decimal_internal_p_gamma(a, x, loggamma_a, scale, rounding=:down)
|
10
|
+
if x >= 1 + a
|
11
|
+
y = 1 - _decimal_internal_q_gamma(a, x, loggamma_a, scale+1, :down)
|
12
|
+
return y.round(scale, rounding)
|
13
|
+
end
|
14
|
+
return Decimal("0e#{-scale}") if x.zero?
|
15
|
+
y0 = exp(a * log(x, scale+1, :down) - x - loggamma_a, scale+1, :down)
|
16
|
+
y = term = y0.divide(a, scale+1, :down)
|
17
|
+
k = 1
|
18
|
+
loop do
|
19
|
+
tmp = x.divide(a + k, scale+1, :down)
|
20
|
+
term = (term * tmp).floor(scale+1)
|
21
|
+
break if term.zero?
|
22
|
+
y += term
|
23
|
+
k += 1
|
24
|
+
end
|
25
|
+
y.round(scale, rounding)
|
26
|
+
end
|
27
|
+
|
28
|
+
def _decimal_internal_q_gamma(a, x, loggamma_a, scale, rounding=:down)
|
29
|
+
la, lb = 1, 1 + x - a
|
30
|
+
if x < 1 + a
|
31
|
+
y = 1 - _decimal_internal_p_gamma(a, x, loggamma_a, scale+1, :down)
|
32
|
+
return y.round(scale, rounding)
|
33
|
+
end
|
34
|
+
w = exp(a * log(x, scale+1, :down) - x - loggamma_a, scale+1, :down)
|
35
|
+
y = w.divide(lb, scale+1, :down)
|
36
|
+
k = 2
|
37
|
+
loop do
|
38
|
+
tmp0 = (k - 1 - a) * (lb - la) + (k + x) * lb
|
39
|
+
tmp = tmp0.divide(k, scale+1, :down)
|
40
|
+
la, lb = lb, tmp
|
41
|
+
w0 = (k - 1 - a).divide(k, scale+1, :down)
|
42
|
+
w = (w * w0).floor(scale+1)
|
43
|
+
term = w.divide(la * lb, scale+1, :down)
|
44
|
+
break if term.zero?
|
45
|
+
y += term
|
46
|
+
end
|
47
|
+
y.round(scale, rounding)
|
48
|
+
end
|
49
|
+
|
50
|
+
def _decimal_internal_bernoulli(x, scale, rounding=:down)
|
51
|
+
return Decimal(1) if x == 0
|
52
|
+
return Decimal("-0.5") if x == 1
|
53
|
+
return Decimal(0) unless x % 2 == 0
|
54
|
+
|
55
|
+
t = Hash.new(0)
|
56
|
+
t[1] = 1
|
57
|
+
q = 1
|
58
|
+
n = 2
|
59
|
+
sign = -1
|
60
|
+
loop do
|
61
|
+
(1...n).each do |i|
|
62
|
+
t[i-1] = i * t[i]
|
63
|
+
end
|
64
|
+
t[n-1] = 0
|
65
|
+
n.downto(2) do |i|
|
66
|
+
t[i] += t[i - 2]
|
67
|
+
end
|
68
|
+
if n.even?
|
69
|
+
sign = -sign
|
70
|
+
q *= 4
|
71
|
+
b1 = n * t[0]
|
72
|
+
b2 = q * (q - 1)
|
73
|
+
return sign * Decimal(b1).divide(b2, scale, rounding) if n == x
|
74
|
+
end
|
75
|
+
n += 1
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def _decimal_internal_fact(n)
|
80
|
+
return 1 if n < 2
|
81
|
+
(2..n).inject {|a, x| a * x}
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# public functions
|
86
|
+
#
|
87
|
+
|
88
|
+
def sqrt(x, scale, rounding=:down)
|
89
|
+
x = Decimal(x) if x.integer?
|
90
|
+
return Decimal::NAN if x.nan?
|
91
|
+
return Decimal("0e#{-scale}") if x.zero?
|
92
|
+
raise Errno::EDOM if x < 0 # XXX
|
93
|
+
return Decimal::INFINITY if x.infinite?
|
94
|
+
|
95
|
+
s = x > 1 ? x : (x + 1).divide(2, scale+1, :down)
|
96
|
+
begin
|
97
|
+
last = s
|
98
|
+
t = x.divide(s, scale+1, :down) + s
|
99
|
+
s = t.divide(2, scale+1, :down)
|
100
|
+
end while s < last
|
101
|
+
last.round(scale, rounding)
|
102
|
+
end
|
103
|
+
|
104
|
+
def cbrt(x, scale, rounding=:down)
|
105
|
+
x = Decimal(x) if x.integer?
|
106
|
+
return x if x.nan? or x.infinite?
|
107
|
+
if x.zero?
|
108
|
+
negative_zero = Decimal(1).divide(x) < 0 # TODO
|
109
|
+
return negative_zero ? Decimal("-0e#{-scale}") : Decimal("0e#{-scale}")
|
110
|
+
end
|
111
|
+
x = -x if negative = x < 0
|
112
|
+
|
113
|
+
s = x > 1 ? x : Decimal(1)
|
114
|
+
begin
|
115
|
+
last = s
|
116
|
+
t = x.divide(s * s, scale+1, :down) + 2 * s
|
117
|
+
s = t.divide(3, scale+1, :down)
|
118
|
+
end while s < last
|
119
|
+
last = -last if negative
|
120
|
+
last.round(scale, rounding)
|
121
|
+
end
|
122
|
+
|
123
|
+
# functions below are copied from BigDecimal:
|
124
|
+
# sin, cos, exp, log, atan, pi
|
125
|
+
|
126
|
+
def sin(x, scale, rounding=:down)
|
127
|
+
x = Decimal(x) if x.integer?
|
128
|
+
return Decimal::NAN if x.infinite? or x.nan?
|
129
|
+
return Decimal("0e#{-scale}") if x.zero?
|
130
|
+
x = -x if negative = x < 0
|
131
|
+
x1 = x
|
132
|
+
x2 = x * x
|
133
|
+
sign = 1
|
134
|
+
y = x
|
135
|
+
i = z = 1
|
136
|
+
loop do
|
137
|
+
sign = -sign
|
138
|
+
x1 *= x2
|
139
|
+
i += 2
|
140
|
+
z *= (i - 1) * i
|
141
|
+
d = sign * x1.divide(z, scale+1, :down)
|
142
|
+
break if d.zero?
|
143
|
+
y += d
|
144
|
+
end
|
145
|
+
y = -y if negative
|
146
|
+
y.round(scale, rounding)
|
147
|
+
end
|
148
|
+
|
149
|
+
def cos(x, scale, rounding=:down)
|
150
|
+
x = Decimal(x) if x.integer?
|
151
|
+
return Decimal::NAN if x.infinite? or x.nan?
|
152
|
+
x = -x if x < 0
|
153
|
+
x1 = 1
|
154
|
+
x2 = x * x
|
155
|
+
sign = 1
|
156
|
+
y = Decimal(1)
|
157
|
+
i = 0
|
158
|
+
z = 1
|
159
|
+
loop do
|
160
|
+
sign = -sign
|
161
|
+
x1 *= x2
|
162
|
+
i += 2
|
163
|
+
z *= (i - 1) * i
|
164
|
+
d = sign * x1.divide(z, scale+1, :down)
|
165
|
+
break if d.zero?
|
166
|
+
y += d
|
167
|
+
end
|
168
|
+
y.round(scale, rounding)
|
169
|
+
end
|
170
|
+
|
171
|
+
def tan(x, scale, rounding=:down)
|
172
|
+
x = Decimal(x) if x.integer?
|
173
|
+
return Decimal::NAN if x.infinite? or x.nan?
|
174
|
+
return Decimal("0e#{-scale}") if x.zero?
|
175
|
+
raise Errno::EDOM if x.infinite? # XXX
|
176
|
+
s, c = sin(x, scale+1), cos(x, scale+1)
|
177
|
+
s.divide(c, scale, rounding)
|
178
|
+
end
|
179
|
+
|
180
|
+
def exp(x, scale, rounding=:down)
|
181
|
+
x = Decimal(x) if x.integer?
|
182
|
+
return Decimal::NAN if x.infinite? or x.nan?
|
183
|
+
return Decimal(1) if x.zero?
|
184
|
+
if x.infinite?
|
185
|
+
return x > 0 ? Decimal::INFINITY : Decimal("0e#{-scale}")
|
186
|
+
end
|
187
|
+
x = -x if negative = x < 0
|
188
|
+
z = x1 = y = 1
|
189
|
+
i = 0
|
190
|
+
loop do
|
191
|
+
x1 *= x
|
192
|
+
i += 1
|
193
|
+
z *= i
|
194
|
+
d = x1.divide(z, scale+1, :down)
|
195
|
+
break if d.zero?
|
196
|
+
y += d
|
197
|
+
end
|
198
|
+
unless negative
|
199
|
+
y.round(scale, rounding)
|
200
|
+
else
|
201
|
+
Decimal(1).divide(y, scale, rounding)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def e(scale, rounding=:down)
|
206
|
+
exp(1, scale, rounding)
|
207
|
+
end
|
208
|
+
|
209
|
+
def log(x, *args) # args:(base=nil, scale, rounding=:down)
|
210
|
+
case args.size
|
211
|
+
when 1
|
212
|
+
base = nil
|
213
|
+
scale = args[0]
|
214
|
+
rounding = :down
|
215
|
+
when 2
|
216
|
+
case args[1]
|
217
|
+
when Symbol
|
218
|
+
base = nil
|
219
|
+
scale, rounding = *args
|
220
|
+
unless scale.is_a?(Integer)
|
221
|
+
raise TypeError, "scale #{scale} must be Integer"
|
222
|
+
end
|
223
|
+
when Integer
|
224
|
+
base, scale = *args
|
225
|
+
unless base.is_a?(Integer)
|
226
|
+
raise TypeError, "base #{base} must be Integer"
|
227
|
+
end
|
228
|
+
rounding = :down
|
229
|
+
else
|
230
|
+
raise ArgumentError, "3rd argument #{args[1]} must be Integer or Symbol"
|
231
|
+
end
|
232
|
+
when 3
|
233
|
+
base, scale, rounding = *args
|
234
|
+
else
|
235
|
+
raise ArgumentError, "wrong number of arguments (#{args.size+1} for 2..4)"
|
236
|
+
end
|
237
|
+
|
238
|
+
x = Decimal(x) if x.integer?
|
239
|
+
return Decimal::NAN if x.nan?
|
240
|
+
return Decimal("0e#{-scale}") if x == 1
|
241
|
+
raise Errno::EDOM if x < 0 # XXX
|
242
|
+
raise Errno::ERANGE if x.zero? # XXX
|
243
|
+
return Decimal::INFINITY if x.infinite?
|
244
|
+
|
245
|
+
if x > 10 or x < Decimal("0.1")
|
246
|
+
x2, n = frexp10(x)
|
247
|
+
y = n * log(10, base, scale+1, :down) + log(x2, base, scale+1, :down)
|
248
|
+
return y.round(scale, rounding)
|
249
|
+
end
|
250
|
+
|
251
|
+
u = (x - 1).divide(x + 1, scale+1, :down)
|
252
|
+
u2 = u * u
|
253
|
+
y = u
|
254
|
+
i = 1
|
255
|
+
loop do
|
256
|
+
u = (u * u2).floor(scale+1)
|
257
|
+
i += 2
|
258
|
+
d = u.divide(i, scale+1, :down)
|
259
|
+
break if d.zero?
|
260
|
+
y += d
|
261
|
+
end
|
262
|
+
y *= 2
|
263
|
+
if base
|
264
|
+
divisor = log(base, scale+1, :down) # divisor = ln(base)
|
265
|
+
y = y.divide(divisor, scale+1, :down)
|
266
|
+
end
|
267
|
+
y.round(scale, rounding)
|
268
|
+
end
|
269
|
+
|
270
|
+
# internal use only
|
271
|
+
@@_decimal_internal_sqrt_01 = Decimal("0.316227766016838")
|
272
|
+
@@_decimal_internal_sqrt_10 = Decimal("3.16227766016838")
|
273
|
+
# TODO: better thresholds needed
|
274
|
+
def log10(x, scale, rounding=:down)
|
275
|
+
x = Decimal(x) if x.integer?
|
276
|
+
return Decimal::INFINITY if x.infinite?
|
277
|
+
if x > @@_decimal_internal_sqrt_10 or x < @@_decimal_internal_sqrt_01
|
278
|
+
x2, n = frexp10(x)
|
279
|
+
(log(x2, 10, scale+1) + n).round(scale, rounding)
|
280
|
+
else
|
281
|
+
log(x, 10, scale, rounding)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def log2(x, scale, rounding=:down)
|
286
|
+
log(x, 2, scale, rounding)
|
287
|
+
end
|
288
|
+
|
289
|
+
def pi(scale, rounding=:down)
|
290
|
+
pi = 0
|
291
|
+
|
292
|
+
k = 1
|
293
|
+
t = Decimal(-80)
|
294
|
+
u = nil
|
295
|
+
loop do
|
296
|
+
t = t.divide(-25, scale+1, :down)
|
297
|
+
u = t.divide(k, scale+1, :down)
|
298
|
+
break if u.zero?
|
299
|
+
pi += u
|
300
|
+
k += 2
|
301
|
+
end
|
302
|
+
|
303
|
+
k = 1
|
304
|
+
t = Decimal(956)
|
305
|
+
loop do
|
306
|
+
t = t.divide(-57121, scale+1, :down)
|
307
|
+
u = t.divide(k, scale+1, :down)
|
308
|
+
break if u.zero?
|
309
|
+
pi += u
|
310
|
+
k += 2
|
311
|
+
end
|
312
|
+
pi.round(scale, rounding)
|
313
|
+
end
|
314
|
+
|
315
|
+
def atan(x, scale, rounding=:down)
|
316
|
+
x = Decimal(x) if x.integer?
|
317
|
+
return Decimal::NAN if x.nan?
|
318
|
+
return Decimal("0e#{-scale}") if x.zero?
|
319
|
+
x = -x if negative = x < 0
|
320
|
+
local_pi = lambda{pi(scale+1, :down)}
|
321
|
+
return local_pi[].divide(negative ? -2 : 2, scale, rounding) if x.infinite?
|
322
|
+
return local_pi[].divide(negative ? -4 : 4, scale, rounding) if x == 1
|
323
|
+
x = Decimal(1).divide(x, scale+1, :down) if inverse = x > 1
|
324
|
+
if double = x > Decimal("0.5")
|
325
|
+
inx = -1 + sqrt(1 + x * x, scale+1, :down)
|
326
|
+
x = inx.divide(x, scale+1,:down)
|
327
|
+
end
|
328
|
+
t = y = x
|
329
|
+
r = 3
|
330
|
+
x2 = (x * x).floor(scale+1)
|
331
|
+
loop do
|
332
|
+
t = ((-t) * x2).floor(scale+1)
|
333
|
+
d = t.divide(r, scale+1, :down)
|
334
|
+
break if d.zero?
|
335
|
+
y += d
|
336
|
+
r += 2
|
337
|
+
end
|
338
|
+
y *= 2 if double
|
339
|
+
y = local_pi[].divide(2, scale+1, :down) - y if inverse
|
340
|
+
y = -y if negative
|
341
|
+
y.round(scale, rounding)
|
342
|
+
end
|
343
|
+
|
344
|
+
def asin(x, scale, rounding=:down)
|
345
|
+
x = Decimal(x) if x.integer?
|
346
|
+
return Decimal::NAN if x.nan?
|
347
|
+
return Decimal("0e#{-scale}") if x.zero?
|
348
|
+
raise Errno::EDOM if x > 1 or x < -1 # XXX
|
349
|
+
x2 = sqrt(1 - x * x, scale+1, :down)
|
350
|
+
atan(x.divide(x2, scale+1, :down), scale, rounding)
|
351
|
+
end
|
352
|
+
|
353
|
+
def acos(x, scale, rounding=:down)
|
354
|
+
x = Decimal(x) if x.integer?
|
355
|
+
return Decimal::NAN if x.nan?
|
356
|
+
return Decimal("0e#{-scale}") if x == 1
|
357
|
+
raise Errno::EDOM if x.infinite? or x > 1 or x < -1 # XXX
|
358
|
+
pi_half = pi(scale+1, :down).divide(2, scale+1, :down)
|
359
|
+
as = asin(x, scale+1, :down)
|
360
|
+
(pi_half - as).round(scale, rounding)
|
361
|
+
end
|
362
|
+
|
363
|
+
def atan2(y, x, scale, rounding=:down)
|
364
|
+
x = Decimal(x) if x.integer?
|
365
|
+
y = Decimal(y) if y.integer?
|
366
|
+
return Decimal::NAN if x.nan? or y.nan?
|
367
|
+
if y_sign = y.infinite?
|
368
|
+
z = pi(scale+1, :down)
|
369
|
+
divisor = nil
|
370
|
+
if x_sign = x.infinite?
|
371
|
+
z *= 3 if x_sign < 0
|
372
|
+
divisor = 4
|
373
|
+
else
|
374
|
+
divisor = 2
|
375
|
+
end
|
376
|
+
return y_sign * z.divide(divisor, scale, rounding)
|
377
|
+
elsif y.zero?
|
378
|
+
y_sign = Decimal(1).divide(y) < 0
|
379
|
+
x_negative = x < 0 or (x.zero? and Decimal(1).divide(x) < 0)
|
380
|
+
z = x_negative ? pi(scale, rounding) : Decimal("0e#{-scale}")
|
381
|
+
return y_sign ? -z : z
|
382
|
+
elsif x.zero?
|
383
|
+
z = pi(scale+1, :down).divide(2, scale, rounding)
|
384
|
+
return y < 0 ? -z : z
|
385
|
+
elsif x_sign = x.infinite?
|
386
|
+
z = x_sign < 0 ? pi(scale, rounding) : Decimal("0e#{-scale}")
|
387
|
+
return y < 0 ? -z : z
|
388
|
+
end
|
389
|
+
|
390
|
+
z = x.divide(y, scale+1, :down)
|
391
|
+
atan(z, scale, rounding)
|
392
|
+
end
|
393
|
+
|
394
|
+
def sinh(x, scale, rounding=:down)
|
395
|
+
x = Decimal(x) if x.integer?
|
396
|
+
return Decimal::NAN if x.nan?
|
397
|
+
return x if x.infinite?
|
398
|
+
return Decimal("0e#{-scale}") if x.zero?
|
399
|
+
e_x = exp(x, scale+1, :down)
|
400
|
+
inv_e_x = Decimal(1).divide(e_x, scale+1, :down)
|
401
|
+
(e_x - inv_e_x).divide(2, scale, rounding)
|
402
|
+
end
|
403
|
+
|
404
|
+
def cosh(x, scale, rounding=:down)
|
405
|
+
x = Decimal(x) if x.integer?
|
406
|
+
return Decimal::NAN if x.nan?
|
407
|
+
return Decimal::INFINITY if x.infinite?
|
408
|
+
if x.zero?
|
409
|
+
return scale > 0 ? Decimal(1) : Decimal("1e#{-scale}")
|
410
|
+
end
|
411
|
+
e_x = exp(x, scale+1, :down)
|
412
|
+
inv_e_x = Decimal(1).divide(e_x, scale+1, :down)
|
413
|
+
(e_x + inv_e_x).divide(2, scale, rounding)
|
414
|
+
end
|
415
|
+
|
416
|
+
def tanh(x, scale, rounding=:down)
|
417
|
+
x = Decimal(x) if x.integer?
|
418
|
+
return Decimal::NAN if x.nan?
|
419
|
+
return Decimal("0e#{-scale}") if x.zero?
|
420
|
+
if y = x.infinite?
|
421
|
+
return y
|
422
|
+
end
|
423
|
+
s, c = sinh(x, scale+1, :down), cosh(x, scale+1, :down)
|
424
|
+
s.divide(c, scale, rounding)
|
425
|
+
end
|
426
|
+
|
427
|
+
def asinh(x, scale, rounding=:down)
|
428
|
+
x = Decimal(x) if x.integer?
|
429
|
+
return Decimal::NAN if x.nan?
|
430
|
+
return Decimal("0e#{-scale}") if x.zero?
|
431
|
+
return x if x.infinite?
|
432
|
+
x2 = sqrt(x * x + 1, scale, :down)
|
433
|
+
log(x + x2, scale, rounding)
|
434
|
+
end
|
435
|
+
|
436
|
+
def acosh(x, scale, rounding=:down)
|
437
|
+
x = Decimal(x) if x.integer?
|
438
|
+
return Decimal::NAN if x.nan?
|
439
|
+
return Decimal("0e#{-scale}") if x == 1
|
440
|
+
raise Errno::EDOM if x < 1
|
441
|
+
return Decimal::INFINITY if x.infinite?
|
442
|
+
x2 = sqrt(x * x - 1, scale, :down)
|
443
|
+
log(x + x2, scale, rounding)
|
444
|
+
end
|
445
|
+
|
446
|
+
def atanh(x, scale, rounding=:down)
|
447
|
+
x = Decimal(x) if x.integer?
|
448
|
+
return Decimal::NAN if x.nan?
|
449
|
+
return Decimal("0e#{-scale}") if x.zero?
|
450
|
+
raise Errno::ERANGE if x == 1 or x == -1 # XXX
|
451
|
+
raise Errno::EDOM if x.abs > 1 # XXX
|
452
|
+
x2 = (1 + x).divide(1 - x, scale+1, :down)
|
453
|
+
y = Decimal("0.5") * log(x2, scale+1, :down)
|
454
|
+
y.round(scale, rounding)
|
455
|
+
end
|
456
|
+
|
457
|
+
def hypot(x, y, scale, rounding=:down)
|
458
|
+
x = Decimal(x) if x.integer?
|
459
|
+
y = Decimal(y) if y.integer?
|
460
|
+
return Decimal::INFINITY if x.infinite? or y.infinite?
|
461
|
+
return Decimal::NAN if x.nan? or y.nan?
|
462
|
+
sqrt(x * x + y * y, scale, rounding)
|
463
|
+
end
|
464
|
+
|
465
|
+
def erf(x, scale, rounding=:down)
|
466
|
+
x = Decimal(x) if x.integer?
|
467
|
+
return Decimal::NAN if x.nan?
|
468
|
+
if y = x.infinite?
|
469
|
+
return y
|
470
|
+
end
|
471
|
+
half = Decimal("0.5")
|
472
|
+
half_log_pi = half * log(pi(scale+1, :down), scale+1, :down)
|
473
|
+
x2 = (x * x).floor(scale+1)
|
474
|
+
y = if x >= 0
|
475
|
+
_decimal_internal_p_gamma(half, x2, half_log_pi, scale+1)
|
476
|
+
else
|
477
|
+
-_decimal_internal_p_gamma(half, x2, half_log_pi, scale+1)
|
478
|
+
end
|
479
|
+
y.round(scale, rounding)
|
480
|
+
end
|
481
|
+
|
482
|
+
def erfc(x, scale, rounding=:down)
|
483
|
+
x = Decimal(x) if x.integer?
|
484
|
+
return Decimal::NAN if x.nan?
|
485
|
+
if x.zero?
|
486
|
+
return scale > 0 ? Decimal("1."+"0"*scale) : Decimal(1)
|
487
|
+
end
|
488
|
+
if y = x.infinite?
|
489
|
+
if y > 0
|
490
|
+
return Decimal("0e#{-scale}")
|
491
|
+
else
|
492
|
+
return scale > 0 ? Decimal("2."+"0"*scale) : Decimal(2)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
half = Decimal("0.5")
|
496
|
+
half_log_pi = half * log(pi(scale+1, :down), scale+1, :down)
|
497
|
+
x2 = (x * x).floor(scale+1)
|
498
|
+
y = if x >= 0
|
499
|
+
_decimal_internal_q_gamma(half, x2, half_log_pi, scale+1)
|
500
|
+
else
|
501
|
+
1 + _decimal_internal_p_gamma(half, x2, half_log_pi, scale+1)
|
502
|
+
end
|
503
|
+
y.round(scale, rounding)
|
504
|
+
end
|
505
|
+
|
506
|
+
def gamma(x, scale, rounding=:down)
|
507
|
+
x = Decimal(x) if x.integer?
|
508
|
+
return Decimal::NAN if x.nan?
|
509
|
+
if inf = x.infinite?
|
510
|
+
return Decimal::INFINITY if inf > 0
|
511
|
+
raise Errno::EDOM # XXX
|
512
|
+
end
|
513
|
+
raise Errno::EDOM if x < 0 and x % 1 == 0 # XXX
|
514
|
+
raise Errno::ERANGE if x.zero? # XXX
|
515
|
+
|
516
|
+
if x < 0
|
517
|
+
pi_tmp = pi(scale+2, :down)
|
518
|
+
pi_x = (pi_tmp * x).floor(scale+2)
|
519
|
+
div1 = sin(pi_x, scale+2, :down)
|
520
|
+
div2 = exp(lgamma(1 - x, scale+2, :down)[0], scale+2, :down)
|
521
|
+
divisor = (div1 * div2).floor(scale+2)
|
522
|
+
return pi_tmp.divide(divisor, scale, rounding)
|
523
|
+
end
|
524
|
+
return Decimal(_decimal_internal_fact(x.to_i - 1)) if (x % 1).zero?
|
525
|
+
exp(lgamma(x, scale+2, :down)[0], scale, rounding)
|
526
|
+
end
|
527
|
+
|
528
|
+
def lgamma(x, scale, rounding=:down)
|
529
|
+
x = Decimal(x) if x.integer?
|
530
|
+
sign = 1
|
531
|
+
return [Decimal::NAN, sign] if x.nan?
|
532
|
+
return [Decimal("0e#{-scale}"), sign] if x == 1 or x == 2
|
533
|
+
if inf = x.infinite?
|
534
|
+
raise Errno::EDOM if inf < 0
|
535
|
+
return [Decimal::INFINITY, sign]
|
536
|
+
end
|
537
|
+
|
538
|
+
if (x % 1).zero?
|
539
|
+
raise Errno::ERANGE if x <= 0 # XXX
|
540
|
+
return [log(_decimal_internal_fact(x.to_i-1), scale, rounding), sign]
|
541
|
+
end
|
542
|
+
sign = -1 if x < 0 and x % 2 > 1
|
543
|
+
threshold = scale # XXX
|
544
|
+
v = 1
|
545
|
+
while x < threshold
|
546
|
+
v *= x
|
547
|
+
x += 1
|
548
|
+
end
|
549
|
+
half = Decimal("0.5")
|
550
|
+
y = (x - half) * log(x, scale+2, :down) - x +
|
551
|
+
half * log(2 * pi(scale+2, :down), scale+2, :down) -
|
552
|
+
log(v.abs, scale+2, :down)
|
553
|
+
n = 2
|
554
|
+
x2 = x * x
|
555
|
+
w = x
|
556
|
+
loop do
|
557
|
+
bn = _decimal_internal_bernoulli(n, scale+2)
|
558
|
+
term = bn.divide(n * (n - 1) * w, scale+2, :down)
|
559
|
+
break if term.zero?
|
560
|
+
y += term
|
561
|
+
n += 2
|
562
|
+
w *= x2
|
563
|
+
end
|
564
|
+
[y.round(scale, rounding), sign]
|
565
|
+
end
|
566
|
+
end
|