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/COPYING
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
Decimal is copyrighted free software by Tadashi Saito <
|
2
|
-
|
1
|
+
Decimal is copyrighted free software by Tadashi Saito <tad.a.diggier[at]
|
2
|
+
gmail.com>.
|
3
3
|
You can redistribute it and/or modify it under either the terms of the GPL
|
4
4
|
version 2 (see the file GPL), or the conditions below:
|
5
5
|
|
data/INSTALL
CHANGED
data/README
CHANGED
@@ -1 +1,57 @@
|
|
1
|
-
|
1
|
+
= Decimal - multi-precision decimal arithmetic library
|
2
|
+
|
3
|
+
Decimal is (yet another) multi-precision decimal arithmetic library,
|
4
|
+
which aims to surpass BigDecimal. It provides simple, compact, fast,
|
5
|
+
precise, stable and easy-to-use solution.
|
6
|
+
|
7
|
+
== Webpages
|
8
|
+
|
9
|
+
* {Home}[http://decimal.rubyforge.org/]
|
10
|
+
* {Latest API Documents}[http://decimal.rubyforge.org/rdoc/]
|
11
|
+
* {Project Page at RubyForge}[http://rubyforge.org/projects/decimal/]
|
12
|
+
* {Mailing Lists}[http://rubyforge.org/mail/?group_id=1994]
|
13
|
+
|
14
|
+
== Requirements
|
15
|
+
|
16
|
+
Ruby 1.8.6 / 1.8.7 / 1.9.1 / 1.9.2dev (experimental)
|
17
|
+
|
18
|
+
== Install
|
19
|
+
|
20
|
+
With rubygems,
|
21
|
+
|
22
|
+
sudo gem install decimal
|
23
|
+
|
24
|
+
or to build by yourself,
|
25
|
+
|
26
|
+
ruby extconf.rb
|
27
|
+
make
|
28
|
+
sudo make install
|
29
|
+
|
30
|
+
Read INSTALL for less details.
|
31
|
+
|
32
|
+
== Examples
|
33
|
+
|
34
|
+
Use like Float with few exceptions including Decimal#divide.
|
35
|
+
|
36
|
+
require 'decimal'
|
37
|
+
|
38
|
+
N = 3 # larger N may take huge time and more inaccurate result
|
39
|
+
pi = 0
|
40
|
+
i = 0
|
41
|
+
|
42
|
+
loop do
|
43
|
+
term = (-1) ** i * Decimal(4).divide(2 * i + 1, N + 2, :down)
|
44
|
+
break if term.zero?
|
45
|
+
pi += term
|
46
|
+
i += 1
|
47
|
+
end
|
48
|
+
|
49
|
+
puts pi.round(N)
|
50
|
+
|
51
|
+
== License
|
52
|
+
|
53
|
+
Ruby's. See COPYING and GPL for more details.
|
54
|
+
|
55
|
+
== Author
|
56
|
+
|
57
|
+
{Tadashi Saito}[mailto:tad.a.digger(at)gmail.com]
|
data/README.1st
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ALL YOUR DECIMAL BASE ARE BELONG TO US
|
data/TODO
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
* Preserve scale of zeros
|
2
|
+
* Implement "decimal/compat/bigdecimal", a bigdecimal-compatible layer
|
3
|
+
* Implement decimal ** otherdecimal, decimal ** -integer
|
4
|
+
(with Decimal#power(other, scale=decimal.scale, mode=Decimal::ROUND_XXX))
|
5
|
+
* Be a good friend with other Numeric classes (using Kernel.autoload?)
|
6
|
+
- OtherNumerics#to_decimal, Decimal#to_*
|
7
|
+
* Define `marshal_dump' and `marshal_load'
|
8
|
+
|
9
|
+
* Implement Decimal.pi / Decimal.e with a precise option
|
10
|
+
* Decimal#format_s based on format of Excel
|
11
|
+
* Embedded decimal32/64 (that size equals to pointer's)
|
12
|
+
* GMP support with "--with-gmp" and inumgmp.h
|
13
|
+
* Static value for well-used value like 0, 1 or 10 ?
|
14
|
+
|
15
|
+
done:
|
16
|
+
- Implement Math functions
|
17
|
+
- Implement webpages
|
18
|
+
- Support RubyGems
|
19
|
+
- Support Ruby 1.9
|
20
|
+
- Implement NaN/+-Infinity as a singleton object
|
21
|
+
- Write tests
|
22
|
+
- Write RDocs
|
23
|
+
- Implement decimal ** fixnum
|
24
|
+
- Create .document
|
25
|
+
|
data/decimal.c
CHANGED
@@ -2,40 +2,39 @@
|
|
2
2
|
* decimal.c - implementation of Decimal,
|
3
3
|
* a multi-precision decimal arithmetic library
|
4
4
|
*
|
5
|
-
* Copyright (C) 2003-
|
5
|
+
* Copyright (C) 2003-2010 Tadashi Saito
|
6
6
|
*
|
7
|
-
* This program is free software; you can
|
8
|
-
*
|
9
|
-
*
|
10
|
-
* 2. redistribute copies so you can help your neighbor.
|
11
|
-
* 3. improve the program, and release your improvements
|
12
|
-
* to the public, so that the whole community benefits.
|
13
|
-
* with the accessibility to the source code.
|
14
|
-
*
|
15
|
-
* This program is licensed under the terms of the Ruby License.
|
16
|
-
* See the file "COPYING" for more details.
|
7
|
+
* This program is free software; you can redistribute it and/or modify
|
8
|
+
* it under the terms of the Ruby License. See the file "COPYING" for
|
9
|
+
* more details.
|
17
10
|
*/
|
18
11
|
|
19
12
|
#include <ctype.h>
|
20
13
|
#include <float.h>
|
14
|
+
#define _ISOC99_SOURCE
|
21
15
|
#include <math.h>
|
22
16
|
#include <stdio.h>
|
23
17
|
#include <string.h>
|
24
18
|
|
25
19
|
#include <ruby.h>
|
26
|
-
#
|
27
|
-
#include <
|
20
|
+
#ifdef HAVE_RUBY_UTIL_H
|
21
|
+
#include <ruby/util.h>
|
22
|
+
#else
|
28
23
|
#include <util.h>
|
24
|
+
#endif
|
25
|
+
|
26
|
+
/* we need support both of 1.8/1.9 with the same source! */
|
27
|
+
#include "ruby18compat.h"
|
29
28
|
|
30
29
|
/*
|
31
|
-
* unfortunately,
|
30
|
+
* unfortunately, few copies of Integer functions
|
32
31
|
* are needed from original Ruby
|
33
32
|
*/
|
34
|
-
#include
|
33
|
+
#include INUM_SOURCE_FILE
|
35
34
|
|
36
35
|
/*
|
37
36
|
* INUM_* macros: receive both Fixnum and Bignum,
|
38
|
-
*
|
37
|
+
* to operate any types of Integers transparently
|
39
38
|
*/
|
40
39
|
#define INUM_PLUS(a, b) \
|
41
40
|
(FIXNUM_P(a) ? fix_plus(a, b) : rb_big_plus(a, b))
|
@@ -44,27 +43,32 @@
|
|
44
43
|
#define INUM_MUL(a, b) \
|
45
44
|
(FIXNUM_P(a) ? fix_mul(a, b) : rb_big_mul(a, b))
|
46
45
|
#define INUM_DIV(a, b) \
|
47
|
-
(FIXNUM_P(a) ? fix_div(a, b) :
|
46
|
+
(FIXNUM_P(a) ? fix_div(a, b) : rb_big_div(a, b))
|
48
47
|
#define INUM_DIVMOD(a, b) \
|
49
48
|
(FIXNUM_P(a) ? fix_divmod(a, b) : rb_big_divmod(a, b))
|
49
|
+
#define INUM_POW(a, b) \
|
50
|
+
(FIXNUM_P(a) ? fix_pow(a, b) : rb_big_pow(a, b))
|
50
51
|
#define INUM_EQ(a, b) \
|
51
52
|
(FIXNUM_P(a) ? fix_equal(a, b) : rb_big_eq(a, b))
|
52
53
|
#define INUM_CMP(a, b) \
|
53
54
|
(FIXNUM_P(a) ? fix_cmp(a, b) : rb_big_cmp(a, b))
|
55
|
+
#define INUM_GT(a, b) (FIX2INT(INUM_CMP(a, b)) > 0)
|
54
56
|
#define INUM_UMINUS(n) \
|
55
|
-
(FIXNUM_P(n) ? LONG2NUM(-FIX2LONG(n)) :
|
57
|
+
(FIXNUM_P(n) ? LONG2NUM(-FIX2LONG(n)) : rb_big_uminus(n))
|
56
58
|
#define INUM_HASH(n) \
|
57
|
-
(FIXNUM_P(n) ?
|
59
|
+
(FIXNUM_P(n) ? rb_obj_id(n) : rb_big_hash(n))
|
58
60
|
#define INUM2STR(n) \
|
59
61
|
(FIXNUM_P(n) ? rb_fix2str(n, 10) : rb_big2str(n, 10))
|
62
|
+
#define INUM_ODD_P(n) \
|
63
|
+
(FIXNUM_P(n) ? fix_odd_p(n) : rb_big_odd_p(n))
|
60
64
|
|
65
|
+
/* implementation-independent INUM_* macros */
|
61
66
|
#define INUM_INC(n) do { n = INUM_PLUS(n, INT2FIX(1)); } while (0)
|
62
67
|
#define INUM_DEC(n) do { n = INUM_MINUS(n, INT2FIX(1)); } while (0)
|
63
68
|
#define INUM_ZERO_P(n) (FIXNUM_P(n) && FIX2LONG(n) == 0)
|
64
|
-
#define INUM_NEGATIVE_P(n) (FIXNUM_P(n) ? FIX2LONG(n) < 0 :
|
69
|
+
#define INUM_NEGATIVE_P(n) (FIXNUM_P(n) ? FIX2LONG(n) < 0 : RBIGNUM_NEGATIVE_P(n))
|
65
70
|
#define INUM_BOTTOMDIG(n) (FIXNUM_P(n) ? FIX2LONG(n) % 10 : \
|
66
|
-
|
67
|
-
#define INUM_ODD_P(n) (FIXNUM_P(n) ? n & 2 : BDIGITS(n)[0] & 1)
|
71
|
+
!rb_bigzero_p(n) ? FIX2INT(rb_big_modulo(n, INT2FIX(10))) : 0)
|
68
72
|
|
69
73
|
/* the body */
|
70
74
|
typedef struct {
|
@@ -76,23 +80,43 @@ typedef struct {
|
|
76
80
|
static Decimal *const DEC_NaN = (Decimal *)1;
|
77
81
|
static Decimal *const DEC_PINF = (Decimal *)3;
|
78
82
|
static Decimal *const DEC_NINF = (Decimal *)7;
|
79
|
-
/* and their representation as Ruby
|
83
|
+
/* and their representation as Ruby objects */
|
80
84
|
static VALUE VALUE_NaN, VALUE_PINF, VALUE_NINF;
|
81
85
|
|
82
|
-
|
86
|
+
#define CHECK_NAN_WITH_VAL(v, retval) do { \
|
87
|
+
if (v == VALUE_NaN) return retval; } while (0)
|
88
|
+
#define CHECK_NAN2_WITH_VAL(v1, v2, retval) do { \
|
89
|
+
if (v1 == VALUE_NaN || v2 == VALUE_NaN) return retval; } while (0)
|
90
|
+
#define CHECK_NAN(v) CHECK_NAN_WITH_VAL(v, VALUE_NaN)
|
91
|
+
#define CHECK_NAN2(v1, v2) CHECK_NAN2_WITH_VAL(v1, v2, VALUE_NaN)
|
92
|
+
|
93
|
+
/* special constants - i.e. non-zero and non-fixnum */
|
83
94
|
/* used for signed zeros that never meet any fixnums nor normal VALUEs */
|
84
95
|
static const VALUE DEC_PZERO = 2, DEC_NZERO = 6;
|
85
|
-
#define dec_pzero() WrapDecimal(
|
86
|
-
#define dec_nzero() WrapDecimal(
|
96
|
+
#define dec_pzero(scale) WrapDecimal(dec_raw_new(DEC_PZERO, scale))
|
97
|
+
#define dec_nzero(scale) WrapDecimal(dec_raw_new(DEC_NZERO, scale))
|
87
98
|
|
88
99
|
#define DEC_ISINF(d) ((d) == DEC_PINF || (d) == DEC_NINF)
|
100
|
+
#define DEC_VALUE_ISINF(v) ((v) == VALUE_PINF || (v) == VALUE_NINF)
|
89
101
|
/* immediate means non-finite */
|
90
|
-
#define DEC_IMMEDIATE_P(d) (DEC_ISINF(d) || d == DEC_NaN)
|
102
|
+
#define DEC_IMMEDIATE_P(d) (DEC_ISINF(d) || (d) == DEC_NaN)
|
91
103
|
/* special signed zeros */
|
92
|
-
#define DEC_ZERO_P(d) ((d)->inum == DEC_PZERO || (d)->inum == DEC_NZERO)
|
93
104
|
#define INUM_SPZERO_P(n) ((n) == DEC_PZERO || (n) == DEC_NZERO)
|
105
|
+
#define DEC_ZERO_P(d) INUM_SPZERO_P((d)->inum)
|
106
|
+
|
107
|
+
/* use internally in to_f */
|
108
|
+
static Decimal *DEC_DBL_MIN = NULL, *DEC_DBL_MAX = NULL;
|
109
|
+
static VALUE INUM_DBL_MAX = Qnil;
|
110
|
+
#define GET_DEC_DBL_MAX() (DEC_DBL_MAX != NULL ? DEC_DBL_MAX : \
|
111
|
+
(DEC_DBL_MAX = dbl_threshold_to_dec(DBL_MAX)))
|
112
|
+
#define GET_DEC_DBL_MIN() (DEC_DBL_MIN != NULL ? DEC_DBL_MIN : \
|
113
|
+
(DEC_DBL_MIN = dbl_threshold_to_dec(DBL_MIN)))
|
114
|
+
#define GET_INUM_DBL_MAX() (INUM_DBL_MAX != Qnil ? INUM_DBL_MAX : \
|
115
|
+
dbl_threshold_to_inum(DBL_MAX, &INUM_DBL_MAX))
|
94
116
|
|
95
|
-
/*
|
117
|
+
/*
|
118
|
+
* all rounding modes
|
119
|
+
*/
|
96
120
|
static VALUE ROUND_CEILING;
|
97
121
|
static VALUE ROUND_DOWN;
|
98
122
|
static VALUE ROUND_FLOOR;
|
@@ -106,17 +130,15 @@ static VALUE ROUND_UNNECESSARY;
|
|
106
130
|
Data_Get_Struct(obj, Decimal, d); \
|
107
131
|
if (d == NULL) rb_raise(rb_eArgError, "uninitialized Decimal object"); \
|
108
132
|
} while (0)
|
109
|
-
#define WrapDecimal(d)
|
110
|
-
|
111
|
-
#define WrapImmediate(d) \
|
112
|
-
Data_Wrap_Struct(cDecimal, NULL, NULL, d)
|
133
|
+
#define WrapDecimal(d) Data_Wrap_Struct(cDecimal, dec_mark, dec_free, d)
|
134
|
+
#define WrapStatic(d) Data_Wrap_Struct(cDecimal, NULL, NULL, d)
|
113
135
|
#define DECIMAL_P(d) (CLASS_OF(d) == cDecimal)
|
114
136
|
|
115
137
|
static VALUE cDecimal;
|
116
138
|
static VALUE eDomainError;
|
117
139
|
static VALUE eArithmeticError;
|
118
140
|
|
119
|
-
/* mark if d->inum is Bignum */
|
141
|
+
/* mark if d->inum is a Bignum */
|
120
142
|
static void
|
121
143
|
dec_mark(void *p)
|
122
144
|
{
|
@@ -124,7 +146,7 @@ dec_mark(void *p)
|
|
124
146
|
|
125
147
|
if (d == NULL) return; /* uninitialized object */
|
126
148
|
if (!FIXNUM_P(d->inum) && !INUM_SPZERO_P(d->inum)) {
|
127
|
-
|
149
|
+
rb_gc_mark(d->inum); /* mark a Bignum */
|
128
150
|
}
|
129
151
|
}
|
130
152
|
|
@@ -144,18 +166,22 @@ dec_free(void *p)
|
|
144
166
|
}
|
145
167
|
#endif
|
146
168
|
|
147
|
-
static Decimal *
|
148
|
-
|
169
|
+
static inline Decimal *
|
170
|
+
dec_raw_new(VALUE inum, long scale)
|
149
171
|
{
|
150
172
|
Decimal *d = ALLOC(Decimal);
|
151
173
|
|
152
|
-
d =
|
153
|
-
|
154
|
-
else d->inum = x;
|
155
|
-
d->scale = 0;
|
174
|
+
d->inum = inum;
|
175
|
+
d->scale = scale;
|
156
176
|
return d;
|
157
177
|
}
|
158
178
|
|
179
|
+
static Decimal *
|
180
|
+
inum_to_dec(VALUE x)
|
181
|
+
{
|
182
|
+
return dec_raw_new(INUM_ZERO_P(x) ? DEC_PZERO : x, 0);
|
183
|
+
}
|
184
|
+
|
159
185
|
static VALUE
|
160
186
|
cstr_to_inum(VALUE str)
|
161
187
|
{
|
@@ -175,59 +201,46 @@ invalid_str(VALUE arg)
|
|
175
201
|
static Decimal *
|
176
202
|
cstr_to_dec(const char *str)
|
177
203
|
{
|
178
|
-
Decimal *d;
|
179
204
|
char *const s = strdup(str);
|
180
|
-
char *ss
|
205
|
+
char *ss;
|
181
206
|
long scale = 0;
|
182
207
|
VALUE inum, assoc[2];
|
183
208
|
|
184
209
|
assoc[0] = (VALUE)s;
|
185
210
|
assoc[1] = (VALUE)str;
|
186
|
-
if (ss = strpbrk(s, "
|
187
|
-
|
188
|
-
|
189
|
-
|
211
|
+
if (ss = strpbrk(s, "Ee")) {
|
212
|
+
*ss++ = '\0'; /* for strchr() */
|
213
|
+
inum = rb_rescue(cstr_to_inum, (VALUE)ss, invalid_str, (VALUE)assoc);
|
214
|
+
scale -= NUM2LONG(inum);
|
190
215
|
}
|
191
216
|
if (ss = strchr(s, '.')) {
|
192
|
-
|
217
|
+
const char *p;
|
193
218
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
ss2--;
|
199
|
-
} while (*ss2 == '0' && ss2 > s);
|
200
|
-
if (*ss2 == '0' || *ss2 == '-' || *ss2 == '+' || ISSPACE(*ss2))
|
201
|
-
*ss = '0';
|
202
|
-
else
|
203
|
-
out:
|
204
|
-
#endif
|
205
|
-
*ss = '_'; /* so that rb_cstr_to_inum() can ignore '.' */
|
206
|
-
for (p = ss + 1; ISDIGIT(*p) || *p == '_'; p++) {
|
207
|
-
if (ISDIGIT(*p)) scale++;
|
208
|
-
}
|
219
|
+
*ss = '_'; /* so that rb_cstr_to_inum() can ignore '.' */
|
220
|
+
for (p = ss + 1; ISDIGIT(*p) || *p == '_'; p++) {
|
221
|
+
if (ISDIGIT(*p)) scale++;
|
222
|
+
}
|
209
223
|
}
|
210
224
|
inum = rb_rescue(cstr_to_inum, (VALUE)s, invalid_str, (VALUE)assoc);
|
211
|
-
d = ALLOC(Decimal);
|
212
225
|
if (INUM_ZERO_P(inum)) {
|
213
|
-
|
226
|
+
inum = strchr(s, '-') ? DEC_NZERO : DEC_PZERO;
|
214
227
|
}
|
215
|
-
else d->inum = inum;
|
216
|
-
d->scale = scale;
|
217
228
|
xfree(s);
|
218
|
-
return
|
229
|
+
return dec_raw_new(inum, scale);
|
219
230
|
}
|
220
231
|
|
221
232
|
static Decimal *
|
222
|
-
finite_dup(Decimal *d)
|
233
|
+
finite_dup(const Decimal *d)
|
223
234
|
{
|
224
|
-
|
235
|
+
VALUE inum;
|
225
236
|
|
226
|
-
|
227
|
-
|
228
|
-
d2->inum = rb_big_clone(d->inum); /* inum is a Bignum */
|
237
|
+
if (FIXNUM_P(d->inum) || INUM_SPZERO_P(d->inum)) {
|
238
|
+
inum = d->inum;
|
229
239
|
}
|
230
|
-
|
240
|
+
else {
|
241
|
+
inum = rb_big_clone(d->inum); /* inum is a Bignum */
|
242
|
+
}
|
243
|
+
return dec_raw_new(inum, d->scale);
|
231
244
|
}
|
232
245
|
|
233
246
|
static Decimal *
|
@@ -236,80 +249,105 @@ create_dec(VALUE arg)
|
|
236
249
|
switch (TYPE(arg)) {
|
237
250
|
case T_FIXNUM:
|
238
251
|
case T_BIGNUM:
|
239
|
-
|
252
|
+
return inum_to_dec(arg);
|
240
253
|
case T_STRING:
|
241
|
-
|
242
|
-
case T_DATA:
|
243
|
-
if (DECIMAL_P(arg)) {
|
244
|
-
Decimal *d;
|
245
|
-
|
246
|
-
GetDecimal(arg, d);
|
247
|
-
if (DEC_IMMEDIATE_P(d))
|
248
|
-
return d;
|
249
|
-
return finite_dup(d);
|
250
|
-
}
|
251
|
-
/* fall through */
|
254
|
+
return cstr_to_dec(StringValueCStr(arg));
|
252
255
|
case T_FLOAT:
|
253
|
-
|
254
|
-
|
256
|
+
rb_raise(rb_eArgError, "invalid type Float: %s",
|
257
|
+
RSTRING_PTR(rb_inspect(arg)));
|
255
258
|
default:
|
256
|
-
|
257
|
-
|
259
|
+
rb_raise(rb_eArgError, "invalid value for Decimal: %s",
|
260
|
+
RSTRING_PTR(rb_inspect(arg)));
|
258
261
|
}
|
259
262
|
return NULL; /* not reached */
|
260
263
|
}
|
261
264
|
|
262
|
-
|
263
|
-
f_decimal(VALUE klass_unused, VALUE arg)
|
264
|
-
{
|
265
|
-
if (DECIMAL_P(arg)) return arg;
|
266
|
-
return WrapDecimal(create_dec(arg));
|
267
|
-
}
|
268
|
-
|
269
|
-
/* TODO: should know about allocation framework */
|
265
|
+
/* TODO: should know about allocation framework for dumping/loading */
|
270
266
|
static VALUE
|
271
267
|
dec_s_allocate(VALUE klass)
|
272
268
|
{
|
273
269
|
return Data_Wrap_Struct(klass, dec_mark, dec_free, NULL);
|
274
270
|
}
|
275
271
|
|
276
|
-
/*
|
272
|
+
/*
|
273
|
+
* call-seq:
|
274
|
+
* Decimal.new(arg) => decimal
|
275
|
+
*
|
276
|
+
* Returns a new decimal made from _arg_. The _arg_ must be an +Integer+
|
277
|
+
* or a +String+. An acceptable format of +String+ is equal to
|
278
|
+
* <code>Kernel.Float()</code>'s one. In a +Regexp+, it should be:
|
279
|
+
*
|
280
|
+
* digits = /(\d+_)*\d+/
|
281
|
+
* number = /(\+-)?#{digits}/
|
282
|
+
* body = /#{number}(\.#{digits})?([eE]#{number})?/
|
283
|
+
* decimal = /\A\s*#{body}\s*\z/
|
284
|
+
*
|
285
|
+
* And its samples are:
|
286
|
+
*
|
287
|
+
* Decimal(1) #=> Decimal(1)
|
288
|
+
* Decimal(2**64) #=> Decimal(18446744073709551616)
|
289
|
+
* Decimal("1") #=> Decimal(1)
|
290
|
+
* Decimal("1.1") #=> Decimal(1.1)
|
291
|
+
* Decimal("1e10") #=> Decimal(10000000000)
|
292
|
+
* Decimal("299_792_458") #=> Decimal(299792458)
|
293
|
+
* Decimal("2.99_792_458e8") #=> Decimal(299792458)
|
294
|
+
*
|
295
|
+
* Notice that a +Float+ is *not* acceptable for _arg_ to keep exactness.
|
296
|
+
*
|
297
|
+
* Decimal.new(1.1) #=> (ArgumentError)
|
298
|
+
*/
|
277
299
|
static VALUE
|
278
300
|
dec_initialize(VALUE self, VALUE arg)
|
279
301
|
{
|
280
|
-
|
281
|
-
|
282
|
-
if (DEC_IMMEDIATE_P(d)) { /* no need to manage about memory */
|
283
|
-
RDATA(self)->dmark = RDATA(self)->dmark = NULL;
|
302
|
+
if (DECIMAL_P(arg)) {
|
303
|
+
return arg;
|
284
304
|
}
|
285
|
-
DATA_PTR(self) =
|
305
|
+
DATA_PTR(self) = create_dec(arg);
|
286
306
|
return self;
|
287
307
|
}
|
288
308
|
|
309
|
+
#ifdef __GNUC__
|
310
|
+
#define UNUSED __attribute__((unused))
|
311
|
+
#else
|
312
|
+
#define UNUSED /* nothing */
|
313
|
+
#endif
|
314
|
+
/*
|
315
|
+
* call-seq:
|
316
|
+
* Decimal(arg) => decimal
|
317
|
+
*
|
318
|
+
* Identical to <code>Decimal.new(arg)</code>, except that this method
|
319
|
+
* never be affected from overriding <code>Decimal#initialize</code>.
|
320
|
+
*/
|
321
|
+
static VALUE
|
322
|
+
f_decimal(VALUE klass UNUSED, VALUE arg)
|
323
|
+
{
|
324
|
+
return dec_initialize(dec_s_allocate(cDecimal), arg);
|
325
|
+
}
|
326
|
+
|
289
327
|
#ifdef DEBUG
|
328
|
+
/* :nodoc: */
|
290
329
|
static VALUE
|
291
330
|
dec_scale(VALUE self)
|
292
331
|
{
|
293
332
|
Decimal *d;
|
294
333
|
|
295
|
-
GetDecimal(self, d);
|
296
|
-
if (
|
297
|
-
return
|
334
|
+
GetDecimal(self, d);
|
335
|
+
if (DEC_IMMEDIATE_P(d)) return Qnil;
|
336
|
+
return LONG2NUM(d->scale);
|
298
337
|
}
|
299
338
|
|
339
|
+
/* :nodoc: */
|
300
340
|
static VALUE
|
301
341
|
dec_unscaled_value(VALUE self)
|
302
342
|
{
|
303
343
|
Decimal *d;
|
304
344
|
|
305
345
|
GetDecimal(self, d);
|
306
|
-
if (
|
307
|
-
|
308
|
-
}
|
309
|
-
return Qnil;
|
346
|
+
if (DEC_IMMEDIATE_P(d)) return Qnil;
|
347
|
+
return DEC_ZERO_P(d) ? INT2FIX(0) : d->inum;
|
310
348
|
}
|
311
|
-
#endif
|
312
349
|
|
350
|
+
/* :nodoc: */
|
313
351
|
static VALUE
|
314
352
|
dec_strip_trailing_zeros(VALUE self)
|
315
353
|
{
|
@@ -318,14 +356,13 @@ dec_strip_trailing_zeros(VALUE self)
|
|
318
356
|
GetDecimal(self, d);
|
319
357
|
if (DEC_IMMEDIATE_P(d))
|
320
358
|
return self;
|
321
|
-
if (
|
359
|
+
if (DEC_ZERO_P(d)) { /* XXX: negative scale? */
|
322
360
|
if (d->scale <= 0) return self;
|
323
361
|
d2 = finite_dup(d);
|
324
|
-
d2->scale = 0;
|
362
|
+
d2->scale = 0;
|
325
363
|
return WrapDecimal(d2);
|
326
364
|
}
|
327
|
-
d2 =
|
328
|
-
*d2 = *d;
|
365
|
+
d2 = finite_dup(d);
|
329
366
|
/* TODO: can be optimized with dividing each part
|
330
367
|
* for Bignums and Fixnums */
|
331
368
|
while (INUM_BOTTOMDIG(d2->inum) == 0) {
|
@@ -334,14 +371,15 @@ dec_strip_trailing_zeros(VALUE self)
|
|
334
371
|
}
|
335
372
|
return WrapDecimal(d2);
|
336
373
|
}
|
374
|
+
#endif /* DEBUG */
|
337
375
|
|
338
376
|
/* FIXME: should return "%g" format string */
|
339
377
|
static VALUE
|
340
|
-
finite_to_s(Decimal *d)
|
378
|
+
finite_to_s(const Decimal *d)
|
341
379
|
{
|
342
380
|
const VALUE str = INUM2STR(d->inum);
|
343
|
-
const char *s =
|
344
|
-
const long slen =
|
381
|
+
const char *s = RSTRING_PTR(str);
|
382
|
+
const long slen = RSTRING_LEN(str);
|
345
383
|
const long scale = d->scale;
|
346
384
|
long snumlen, sslen, diff;
|
347
385
|
int negative;
|
@@ -380,25 +418,62 @@ finite_to_s(Decimal *d)
|
|
380
418
|
memcpy(ss2+diff, s, snumlen);
|
381
419
|
}
|
382
420
|
coda:
|
383
|
-
newstr =
|
421
|
+
newstr = rb_usascii_str_new(ss, sslen);
|
384
422
|
xfree(ss);
|
385
423
|
return newstr;
|
386
424
|
}
|
387
425
|
|
426
|
+
/*
|
427
|
+
* call-seq:
|
428
|
+
* dec.to_s => string
|
429
|
+
*
|
430
|
+
* *WARNING*: The behavior of this method may change.
|
431
|
+
*
|
432
|
+
* Returns a string containing a simple representation of self.
|
433
|
+
*
|
434
|
+
* Decimal(1).to_s #=> "1"
|
435
|
+
* Decimal("1.1").to_s #=> "1.1"
|
436
|
+
* Decimal::INFINITY.to_s #=> "Infinity"
|
437
|
+
*/
|
388
438
|
static VALUE
|
389
439
|
dec_to_s(VALUE self)
|
390
440
|
{
|
391
441
|
Decimal *d;
|
392
442
|
|
443
|
+
CHECK_NAN_WITH_VAL(self, rb_usascii_str_new_cstr("NaN"));
|
444
|
+
if (self == VALUE_PINF) return rb_usascii_str_new_cstr("Infinity");
|
445
|
+
if (self == VALUE_NINF) return rb_usascii_str_new_cstr("-Infinity");
|
393
446
|
GetDecimal(self, d);
|
394
|
-
if (d
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
447
|
+
if (DEC_ZERO_P(d)) {
|
448
|
+
const size_t HEAD_LEN = d->inum == DEC_PZERO ? 2U : 3U; /* "-0.".length */
|
449
|
+
long len = HEAD_LEN + d->scale;
|
450
|
+
char *buf;
|
451
|
+
|
452
|
+
/* FIXME: use "0eN" style when the scale is negative? */
|
453
|
+
if (d->scale <= 0) /* ignore the case of negative scale */
|
454
|
+
return d->inum == DEC_PZERO ?
|
455
|
+
rb_usascii_str_new_cstr("0") : rb_usascii_str_new_cstr("-0");
|
456
|
+
buf = xmalloc(len);
|
457
|
+
if (d->inum == DEC_PZERO)
|
458
|
+
memcpy(buf, "0.", HEAD_LEN);
|
459
|
+
else
|
460
|
+
memcpy(buf, "-0.", HEAD_LEN);
|
461
|
+
memset(buf + HEAD_LEN, '0', d->scale);
|
462
|
+
return rb_usascii_str_new(buf, len);
|
463
|
+
}
|
399
464
|
return finite_to_s(d);
|
400
465
|
}
|
401
466
|
|
467
|
+
/*
|
468
|
+
* call-seq:
|
469
|
+
* dec.inspect => string
|
470
|
+
*
|
471
|
+
* Returns a easy-to-distinguish string: <code>"Decimal(#{dec})"</code>.
|
472
|
+
*
|
473
|
+
* Decimal(1).inspect #=> "Decimal(1)"
|
474
|
+
* Decimal("1.1").inspect #=> "Decimal(1.1)"
|
475
|
+
* Decimal::INFINITY.inspect #=> "Decimal(Infinity)"
|
476
|
+
*/
|
402
477
|
static VALUE
|
403
478
|
dec_inspect(VALUE self)
|
404
479
|
{
|
@@ -407,14 +482,26 @@ dec_inspect(VALUE self)
|
|
407
482
|
long len;
|
408
483
|
|
409
484
|
str = dec_to_s(self);
|
410
|
-
len = 9 +
|
485
|
+
len = 9 + RSTRING_LEN(str); /* 9 == strlen("Decimal()") */
|
411
486
|
s = ALLOC_N(char, len + 1); /* +1 for NUL */
|
412
|
-
sprintf(s, "Decimal(%s)",
|
413
|
-
newstr =
|
487
|
+
sprintf(s, "Decimal(%s)", RSTRING_PTR(str));
|
488
|
+
newstr = rb_usascii_str_new(s, len);
|
414
489
|
xfree(s);
|
415
490
|
return newstr;
|
416
491
|
}
|
417
492
|
|
493
|
+
/*
|
494
|
+
* call-seq:
|
495
|
+
* dec.coerce(other) => array
|
496
|
+
*
|
497
|
+
* Returns array <code>[Decimal(other), dec]</code> if _other_ has a
|
498
|
+
* compatible type, +Integer+ or +Decimal+.
|
499
|
+
* Otherwise raises a +TypeError+.
|
500
|
+
*
|
501
|
+
* Decimal(1).coerce(2) #=> [Decimal(2), Decimal(1)]
|
502
|
+
* Decimal(1).coerce(Decimal(2)) #=> [Decimal(2), Decimal(1)]
|
503
|
+
* Decimal(1).coerce(2.5) #=> (TypeError)
|
504
|
+
*/
|
418
505
|
static VALUE
|
419
506
|
dec_coerce(VALUE x, VALUE y)
|
420
507
|
{
|
@@ -440,25 +527,30 @@ dec_coerce(VALUE x, VALUE y)
|
|
440
527
|
return Qnil; /* not reached */
|
441
528
|
}
|
442
529
|
|
530
|
+
/*
|
531
|
+
* call-seq:
|
532
|
+
* -dec => decimal
|
533
|
+
*
|
534
|
+
* Returns a negated value of _dec_.
|
535
|
+
*/
|
443
536
|
static VALUE
|
444
537
|
dec_uminus(VALUE num)
|
445
538
|
{
|
446
|
-
|
539
|
+
VALUE inum;
|
540
|
+
Decimal *d;
|
447
541
|
|
448
|
-
|
449
|
-
if (
|
450
|
-
if (
|
451
|
-
if (d == DEC_NINF) return VALUE_PINF;
|
542
|
+
CHECK_NAN(num);
|
543
|
+
if (num == VALUE_PINF) return VALUE_NINF;
|
544
|
+
if (num == VALUE_NINF) return VALUE_PINF;
|
452
545
|
|
453
|
-
|
454
|
-
d2->scale = d->scale;
|
546
|
+
GetDecimal(num, d);
|
455
547
|
if (d->inum == DEC_PZERO)
|
456
|
-
|
548
|
+
inum = DEC_NZERO;
|
457
549
|
else if (d->inum == DEC_NZERO)
|
458
|
-
|
550
|
+
inum = DEC_PZERO;
|
459
551
|
else
|
460
|
-
|
461
|
-
return WrapDecimal(
|
552
|
+
inum = INUM_UMINUS(d->inum);
|
553
|
+
return WrapDecimal(dec_raw_new(inum, d->scale));
|
462
554
|
}
|
463
555
|
|
464
556
|
/* returns x * (10 ** n) */
|
@@ -472,11 +564,10 @@ inum_lshift(VALUE x, long n)
|
|
472
564
|
return INUM_MUL(x, y);
|
473
565
|
}
|
474
566
|
|
475
|
-
/* the "normal" number means "finite and
|
567
|
+
/* the "normal" number means "finite and non-zero" */
|
476
568
|
static Decimal *
|
477
|
-
normal_plus(Decimal *x, Decimal *y, const int add)
|
569
|
+
normal_plus(const Decimal *x, const Decimal *y, const int add)
|
478
570
|
{
|
479
|
-
Decimal *z;
|
480
571
|
VALUE inum;
|
481
572
|
long scale;
|
482
573
|
|
@@ -486,29 +577,34 @@ normal_plus(Decimal *x, Decimal *y, const int add)
|
|
486
577
|
scale = x->scale;
|
487
578
|
}
|
488
579
|
else {
|
489
|
-
Decimal *max, *min;
|
580
|
+
const Decimal *max, *min;
|
490
581
|
VALUE min_inum;
|
491
582
|
|
492
583
|
if (x->scale > y->scale) max = x, min = y;
|
493
584
|
else max = y, min = x;
|
494
585
|
scale = max->scale;
|
495
586
|
min_inum = inum_lshift(min->inum, max->scale - min->scale);
|
496
|
-
if (add) inum = INUM_PLUS(min_inum, max->inum);
|
497
|
-
else if (max == x) inum = INUM_MINUS(max->inum, min_inum);
|
498
|
-
else inum = INUM_MINUS(min_inum, max->inum);
|
587
|
+
if (add) inum = INUM_PLUS(min_inum, max->inum);
|
588
|
+
else if (max == x) inum = INUM_MINUS(max->inum, min_inum);
|
589
|
+
else inum = INUM_MINUS(min_inum, max->inum);
|
499
590
|
}
|
500
591
|
if (INUM_ZERO_P(inum)) inum = DEC_PZERO;
|
501
|
-
|
502
|
-
z->inum = inum;
|
503
|
-
z->scale = scale;
|
504
|
-
return z;
|
592
|
+
return dec_raw_new(inum, scale);
|
505
593
|
}
|
506
594
|
|
595
|
+
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
596
|
+
/*
|
597
|
+
* call-seq:
|
598
|
+
* dec + other => decimal
|
599
|
+
*
|
600
|
+
* Returns a new decimal which is the sum of _dec_ and _other_.
|
601
|
+
*/
|
507
602
|
static VALUE
|
508
603
|
dec_plus(VALUE x, VALUE y)
|
509
604
|
{
|
510
605
|
Decimal *a, *b;
|
511
606
|
|
607
|
+
CHECK_NAN2(x, y);
|
512
608
|
switch (TYPE(y)) {
|
513
609
|
case T_FIXNUM:
|
514
610
|
case T_BIGNUM:
|
@@ -520,40 +616,60 @@ dec_plus(VALUE x, VALUE y)
|
|
520
616
|
case T_DATA:
|
521
617
|
if (DECIMAL_P(y)) {
|
522
618
|
GetDecimal(y, b);
|
523
|
-
if (b == DEC_NaN) return VALUE_NaN;
|
524
619
|
break;
|
525
620
|
}
|
526
621
|
/* fall through */
|
527
622
|
default:
|
528
|
-
return rb_num_coerce_bin(x, y);
|
623
|
+
return rb_num_coerce_bin(x, y, '+');
|
529
624
|
}
|
530
|
-
|
531
|
-
if (
|
532
|
-
|
533
|
-
/* now, x and y are not NaNs */
|
534
|
-
if (DEC_ISINF(a)) {
|
535
|
-
if (DEC_ISINF(b) && a != b) return VALUE_NaN;
|
625
|
+
|
626
|
+
if (DEC_VALUE_ISINF(x)) {
|
627
|
+
if (DEC_VALUE_ISINF(y) && x != y) return VALUE_NaN;
|
536
628
|
return x;
|
537
629
|
}
|
538
|
-
if (
|
539
|
-
|
630
|
+
if (DEC_VALUE_ISINF(y)) return y;
|
631
|
+
/* now, x and y are not NaN nor +-INFINITY */
|
632
|
+
GetDecimal(x, a);
|
633
|
+
if (DEC_ZERO_P(a)) {
|
634
|
+
VALUE inum;
|
635
|
+
|
636
|
+
if (DEC_ZERO_P(b)) {
|
637
|
+
const long scale = MAX(a->scale, b->scale);
|
638
|
+
|
540
639
|
if (a->inum == DEC_NZERO && b->inum == DEC_NZERO)
|
541
|
-
|
542
|
-
return dec_pzero();
|
640
|
+
return dec_nzero(scale);
|
641
|
+
return dec_pzero(scale);
|
543
642
|
}
|
544
|
-
|
643
|
+
if (a->scale <= b->scale)
|
644
|
+
return y;
|
645
|
+
inum = inum_lshift(b->inum, a->scale - b->scale);
|
646
|
+
return WrapDecimal(dec_raw_new(inum, a->scale));
|
647
|
+
}
|
648
|
+
if (DEC_ZERO_P(b)) {
|
649
|
+
VALUE inum;
|
650
|
+
|
651
|
+
if (a->scale >= b->scale)
|
652
|
+
return x;
|
653
|
+
inum = inum_lshift(a->inum, b->scale - a->scale);
|
654
|
+
return WrapDecimal(dec_raw_new(inum, b->scale));
|
545
655
|
}
|
546
|
-
if (DEC_ISINF(b)) return y;
|
547
|
-
if (INUM_SPZERO_P(b->inum)) return x; /* FIXME: ditto */
|
548
656
|
/* "true" means addition */
|
549
657
|
return WrapDecimal(normal_plus(a, b, Qtrue));
|
550
658
|
}
|
551
659
|
|
660
|
+
#define NEGATE_INF(x) ((x) == VALUE_PINF ? VALUE_NINF : VALUE_PINF)
|
661
|
+
/*
|
662
|
+
* call-seq:
|
663
|
+
* dec - other => decimal
|
664
|
+
*
|
665
|
+
* Returns a new float which is the difference of _dec_ and _other_.
|
666
|
+
*/
|
552
667
|
static VALUE
|
553
668
|
dec_minus(VALUE x, VALUE y)
|
554
669
|
{
|
555
670
|
Decimal *a, *b;
|
556
671
|
|
672
|
+
CHECK_NAN2(x, y);
|
557
673
|
switch (TYPE(y)) {
|
558
674
|
case T_FIXNUM:
|
559
675
|
case T_BIGNUM:
|
@@ -565,47 +681,49 @@ dec_minus(VALUE x, VALUE y)
|
|
565
681
|
case T_DATA:
|
566
682
|
if (DECIMAL_P(y)) {
|
567
683
|
GetDecimal(y, b);
|
568
|
-
if (b == DEC_NaN) return VALUE_NaN;
|
569
684
|
break;
|
570
685
|
}
|
571
686
|
/* fall through */
|
572
687
|
default:
|
573
|
-
return rb_num_coerce_bin(x, y);
|
688
|
+
return rb_num_coerce_bin(x, y, '-');
|
574
689
|
}
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
if (DEC_ISINF(a)) {
|
579
|
-
if (a == b) return VALUE_NaN;
|
690
|
+
if (DEC_VALUE_ISINF(x)) {
|
691
|
+
if (x == y) return VALUE_NaN;
|
580
692
|
return x;
|
581
693
|
}
|
582
|
-
if (
|
694
|
+
if (DEC_VALUE_ISINF(y)) return NEGATE_INF(y);
|
695
|
+
|
696
|
+
GetDecimal(x, a);
|
697
|
+
if (DEC_ZERO_P(a)) { /* FIXME: needs refactoring */
|
583
698
|
if (!DEC_ISINF(b) && DEC_ZERO_P(b) && a->inum == b->inum) {
|
584
|
-
|
699
|
+
/* FIXME: UNDER CONSTRUCTION for scaling */
|
700
|
+
return dec_pzero(MAX(a->scale, b->scale));
|
585
701
|
}
|
586
702
|
return dec_uminus(y);
|
587
703
|
}
|
588
|
-
if (DEC_ISINF(b)) return dec_uminus(y);
|
589
704
|
if (DEC_ZERO_P(b)) return x;
|
590
705
|
/* "false" means subtraction */
|
591
706
|
return WrapDecimal(normal_plus(a, b, Qfalse));
|
592
707
|
}
|
593
708
|
|
594
709
|
static Decimal *
|
595
|
-
normal_mul(Decimal *x, Decimal *y)
|
710
|
+
normal_mul(const Decimal *x, const Decimal *y)
|
596
711
|
{
|
597
|
-
|
598
|
-
|
599
|
-
z->inum = INUM_MUL(x->inum, y->inum);
|
600
|
-
z->scale = x->scale + y->scale;
|
601
|
-
return z;
|
712
|
+
return dec_raw_new(INUM_MUL(x->inum, y->inum), x->scale + y->scale);
|
602
713
|
}
|
603
714
|
|
715
|
+
/*
|
716
|
+
* call-seq:
|
717
|
+
* dec * other => decimal
|
718
|
+
*
|
719
|
+
* Returns a new decimal which is the product of _dec_ and _other_.
|
720
|
+
*/
|
604
721
|
static VALUE
|
605
722
|
dec_mul(VALUE x, VALUE y)
|
606
723
|
{
|
607
724
|
Decimal *a, *b;
|
608
725
|
|
726
|
+
CHECK_NAN2(x, y);
|
609
727
|
switch (TYPE(y)) {
|
610
728
|
case T_FIXNUM:
|
611
729
|
/* TODO: can be optimized if y = 0, 1 or -1 */
|
@@ -618,31 +736,29 @@ dec_mul(VALUE x, VALUE y)
|
|
618
736
|
case T_DATA:
|
619
737
|
if (DECIMAL_P(y)) {
|
620
738
|
GetDecimal(y, b);
|
621
|
-
if (b == DEC_NaN) return VALUE_NaN;
|
622
739
|
break;
|
623
740
|
}
|
624
741
|
/* fall through */
|
625
742
|
default:
|
626
|
-
return rb_num_coerce_bin(x, y);
|
743
|
+
return rb_num_coerce_bin(x, y, '*');
|
627
744
|
}
|
628
745
|
GetDecimal(x, a);
|
629
|
-
if (a == DEC_NaN) return VALUE_NaN;
|
630
746
|
|
631
747
|
if (DEC_ISINF(a)) {
|
632
|
-
if (DEC_ISINF(b)) return
|
748
|
+
if (DEC_ISINF(b)) return x == y ? VALUE_PINF : VALUE_NINF;
|
633
749
|
if (DEC_ZERO_P(b)) return VALUE_NaN;
|
634
750
|
if (!INUM_NEGATIVE_P(b->inum)) return x;
|
635
751
|
return dec_uminus(x);
|
636
752
|
}
|
637
753
|
if (DEC_ZERO_P(a)) {
|
638
754
|
if (DEC_ISINF(b)) return VALUE_NaN;
|
639
|
-
if (
|
755
|
+
if (DEC_ZERO_P(b)) {
|
640
756
|
return a->inum == DEC_PZERO ? y : dec_uminus(y);
|
641
757
|
}
|
642
758
|
if (INUM_NEGATIVE_P(b->inum)) return dec_uminus(x);
|
643
759
|
return x;
|
644
760
|
}
|
645
|
-
if (DEC_IMMEDIATE_P(b) ||
|
761
|
+
if (DEC_IMMEDIATE_P(b) || DEC_ZERO_P(b)) {
|
646
762
|
if (INUM_NEGATIVE_P(a->inum)) return dec_uminus(y);
|
647
763
|
return y;
|
648
764
|
}
|
@@ -650,21 +766,21 @@ dec_mul(VALUE x, VALUE y)
|
|
650
766
|
}
|
651
767
|
|
652
768
|
static Decimal *
|
653
|
-
do_round(Decimal *d, long scale, VALUE mode, VALUE *inump)
|
769
|
+
do_round(const Decimal *d, long scale, VALUE mode, VALUE *inump)
|
654
770
|
{
|
655
771
|
Decimal *d2;
|
656
772
|
long diff;
|
657
773
|
int lower;
|
658
774
|
int trailing_nonzero, negative;
|
659
|
-
VALUE inum, inumabs, shift,
|
775
|
+
VALUE inum, inumabs, shift, ary;
|
660
776
|
|
661
777
|
if (d == DEC_PINF) rb_raise(eDomainError, "Infinity");
|
662
778
|
if (d == DEC_NINF) rb_raise(eDomainError, "-Infinity");
|
663
779
|
if (d == DEC_NaN) rb_raise(eDomainError, "NaN");
|
664
780
|
if (INUM_SPZERO_P(d->inum)) {
|
665
781
|
if (inump) {
|
666
|
-
if (scale
|
667
|
-
"scale
|
782
|
+
if (scale > 0) rb_bug("do_round(): "
|
783
|
+
"scale > 0 with Integer request");
|
668
784
|
*inump = INT2FIX(0);
|
669
785
|
return NULL;
|
670
786
|
}
|
@@ -673,11 +789,11 @@ do_round(Decimal *d, long scale, VALUE mode, VALUE *inump)
|
|
673
789
|
return d2;
|
674
790
|
}
|
675
791
|
if (d->scale <= scale) { /* no need to round */
|
676
|
-
/* return Decimal */
|
677
|
-
if (scale) return finite_dup(d);
|
792
|
+
if (scale) return finite_dup(d); /* return Decimal */
|
678
793
|
/* return Integer */
|
679
794
|
if (!inump) /* XXX: may be reached when Decimal(1)/1 */
|
680
|
-
rb_bug("do_round(): not reached[2]");
|
795
|
+
rb_bug("do_round(): not reached[2]");
|
796
|
+
/* FIXME: scaling policy, no need to grow scale? */
|
681
797
|
if (d->scale == 0) *inump = d->inum;
|
682
798
|
else *inump = inum_lshift(d->inum, -d->scale);
|
683
799
|
return NULL;
|
@@ -685,16 +801,16 @@ do_round(Decimal *d, long scale, VALUE mode, VALUE *inump)
|
|
685
801
|
negative = INUM_NEGATIVE_P(d->inum);
|
686
802
|
diff = d->scale - scale;
|
687
803
|
inumabs = negative ? INUM_UMINUS(d->inum) : d->inum;
|
688
|
-
if (mode == ROUND_CEILING ||
|
804
|
+
if (mode == ROUND_CEILING || /* don't need lower digit */
|
689
805
|
mode == ROUND_DOWN ||
|
690
806
|
mode == ROUND_FLOOR ||
|
691
807
|
mode == ROUND_UP ||
|
692
808
|
mode == ROUND_UNNECESSARY) {
|
693
809
|
shift = inum_lshift(INT2FIX(1), diff);
|
694
810
|
ary = INUM_DIVMOD(inumabs, shift);
|
695
|
-
inum =
|
811
|
+
inum = RARRAY_PTR(ary)[0];
|
696
812
|
if (mode == ROUND_DOWN) goto coda;
|
697
|
-
trailing_nonzero = !INUM_ZERO_P(
|
813
|
+
trailing_nonzero = !INUM_ZERO_P(RARRAY_PTR(ary)[1]);
|
698
814
|
if (mode == ROUND_CEILING) {
|
699
815
|
if (!negative && trailing_nonzero) INUM_INC(inum);
|
700
816
|
}
|
@@ -713,16 +829,13 @@ do_round(Decimal *d, long scale, VALUE mode, VALUE *inump)
|
|
713
829
|
else if (mode == ROUND_HALF_DOWN || /* needs lower digit */
|
714
830
|
mode == ROUND_HALF_UP ||
|
715
831
|
mode == ROUND_HALF_EVEN) {
|
716
|
-
if (diff
|
832
|
+
if (diff > 1) { /* needs shift */
|
717
833
|
shift = inum_lshift(INT2FIX(1), diff-1);
|
718
|
-
|
719
|
-
|
720
|
-
ary = INUM_DIVMOD(inumabs,
|
721
|
-
|
722
|
-
|
723
|
-
ary = INUM_DIVMOD(tmp, INT2FIX(10));
|
724
|
-
inum = RARRAY(ary)->ptr[0];
|
725
|
-
lower = FIX2INT(RARRAY(ary)->ptr[1]);
|
834
|
+
inumabs = INUM_DIV(inumabs, shift);
|
835
|
+
}
|
836
|
+
ary = INUM_DIVMOD(inumabs, INT2FIX(10));
|
837
|
+
inum = RARRAY_PTR(ary)[0];
|
838
|
+
lower = FIX2INT(RARRAY_PTR(ary)[1]);
|
726
839
|
if (mode == ROUND_HALF_DOWN) {
|
727
840
|
if (lower > 5) INUM_INC(inum);
|
728
841
|
}
|
@@ -740,46 +853,43 @@ do_round(Decimal *d, long scale, VALUE mode, VALUE *inump)
|
|
740
853
|
}
|
741
854
|
coda:
|
742
855
|
if (negative) inum = INUM_UMINUS(inum);
|
743
|
-
if (scale
|
856
|
+
if (scale <= 0 && inump != NULL) {
|
857
|
+
/* return Integer */
|
858
|
+
if (scale < 0) inum = inum_lshift(inum, -scale);
|
744
859
|
*inump = inum;
|
745
860
|
return NULL;
|
746
861
|
}
|
747
862
|
/* return Decimal */
|
748
|
-
d2 = ALLOC(Decimal);
|
749
863
|
if (INUM_ZERO_P(inum)) {
|
750
|
-
|
751
|
-
|
864
|
+
inum = negative ? DEC_NZERO : DEC_PZERO;
|
865
|
+
scale = 0;
|
752
866
|
}
|
753
|
-
|
754
|
-
d2->inum = inum;
|
755
|
-
d2->scale = scale;
|
756
|
-
}
|
757
|
-
return d2;
|
867
|
+
return dec_raw_new(inum, scale);
|
758
868
|
}
|
759
869
|
|
760
870
|
static Decimal *
|
761
|
-
normal_divide(Decimal *x, Decimal *y, long scale, VALUE mode)
|
871
|
+
normal_divide(const Decimal *x, const Decimal *y, long scale, VALUE mode)
|
762
872
|
{
|
763
|
-
long diff;
|
873
|
+
long diff, z_scale;
|
764
874
|
VALUE xx;
|
765
|
-
Decimal *z
|
875
|
+
Decimal *z;
|
766
876
|
|
767
877
|
diff = x->scale - y->scale;
|
768
878
|
if (diff <= scale) {
|
769
879
|
xx = inum_lshift(x->inum, scale-diff+1); /* +1 for rounding */
|
770
|
-
|
880
|
+
z_scale = scale + 1;
|
771
881
|
}
|
772
882
|
else {
|
773
883
|
/* FIXME: may be a bug...? */
|
774
884
|
xx = x->inum;
|
775
|
-
|
885
|
+
z_scale = diff;
|
776
886
|
}
|
777
|
-
z
|
778
|
-
return do_round(z, scale, mode,
|
887
|
+
z = dec_raw_new(INUM_DIV(xx, y->inum), z_scale);
|
888
|
+
return do_round(z, scale, mode, NULL);
|
779
889
|
}
|
780
890
|
|
781
891
|
static int
|
782
|
-
|
892
|
+
valid_rounding_mode_p(VALUE sym)
|
783
893
|
{
|
784
894
|
if (sym == ROUND_CEILING ||
|
785
895
|
sym == ROUND_DOWN ||
|
@@ -794,25 +904,35 @@ valid_rounding_mode(VALUE sym)
|
|
794
904
|
return Qfalse;
|
795
905
|
}
|
796
906
|
|
907
|
+
/*
|
908
|
+
* call-seq:
|
909
|
+
* dec.divide(other, scale=0, mode=Decimal::ROUND_UNNECESSARY) #=> decimal or integer
|
910
|
+
*
|
911
|
+
* *WARNING*: The behavior of this method may change.
|
912
|
+
*
|
913
|
+
* Returns a new decimal which is the result of dividing _dec_ by _other_.
|
914
|
+
*
|
915
|
+
* *FIXME*: write details
|
916
|
+
*/
|
797
917
|
static VALUE
|
798
918
|
dec_divide(int argc, VALUE *argv, VALUE x)
|
799
919
|
{
|
800
920
|
VALUE y;
|
801
921
|
Decimal *a, *b;
|
802
922
|
VALUE mode = ROUND_UNNECESSARY;
|
803
|
-
long
|
923
|
+
long l, scale = 0; /* FIXME: dummy 0 */
|
804
924
|
VALUE vscale, vmode;
|
805
925
|
|
926
|
+
CHECK_NAN(x);
|
806
927
|
GetDecimal(x, a);
|
807
|
-
if (a == DEC_NaN) return VALUE_NaN; /* no need to check b */
|
808
928
|
|
809
929
|
rb_scan_args(argc, argv, "12", &y, &vscale, &vmode);
|
810
930
|
switch (argc) {
|
811
931
|
case 3:
|
812
932
|
Check_Type(vmode, T_SYMBOL);
|
813
|
-
if (!
|
933
|
+
if (!valid_rounding_mode_p(vmode)) {
|
814
934
|
rb_raise(rb_eArgError, "invalid rounding mode %s",
|
815
|
-
|
935
|
+
RSTRING_PTR(rb_inspect(vmode)));
|
816
936
|
}
|
817
937
|
mode = vmode;
|
818
938
|
/* fall through */
|
@@ -823,15 +943,15 @@ dec_divide(int argc, VALUE *argv, VALUE x)
|
|
823
943
|
if (mode != ROUND_UNNECESSARY) {
|
824
944
|
rb_raise(rb_eArgError, "scale number argument needed");
|
825
945
|
}
|
826
|
-
scale = 0; /* FIXME: dummy */
|
827
946
|
}
|
947
|
+
CHECK_NAN(y);
|
828
948
|
|
829
949
|
switch (TYPE(y)) {
|
830
950
|
case T_FIXNUM:
|
831
951
|
l = FIX2LONG(y);
|
832
952
|
if (l == 0) {
|
833
|
-
if (DEC_ZERO_P(a)) return VALUE_NaN;
|
834
953
|
if (DEC_ISINF(a)) return x;
|
954
|
+
if (DEC_ZERO_P(a)) return VALUE_NaN;
|
835
955
|
return INUM_NEGATIVE_P(a->inum) ? VALUE_NINF : VALUE_PINF;
|
836
956
|
}
|
837
957
|
else if (l == 1) return x;
|
@@ -846,19 +966,18 @@ dec_divide(int argc, VALUE *argv, VALUE x)
|
|
846
966
|
case T_DATA:
|
847
967
|
if (DECIMAL_P(y)) {
|
848
968
|
GetDecimal(y, b);
|
849
|
-
if (b == DEC_NaN) return VALUE_NaN;
|
850
969
|
break;
|
851
970
|
}
|
852
971
|
/* fall through */
|
853
972
|
default:
|
854
|
-
|
973
|
+
return rb_num_coerce_bin(x, y, rb_intern("divide"));
|
855
974
|
}
|
856
|
-
|
975
|
+
|
857
976
|
if (DEC_ISINF(a)) {
|
858
977
|
if (DEC_ISINF(b)) return VALUE_NaN;
|
859
978
|
if (b->inum == DEC_PZERO) return x;
|
860
|
-
if (b->inum == DEC_NZERO) return
|
861
|
-
return INUM_NEGATIVE_P(b->inum) ?
|
979
|
+
if (b->inum == DEC_NZERO) return NEGATE_INF(x);
|
980
|
+
return INUM_NEGATIVE_P(b->inum) ? NEGATE_INF(x) : x;
|
862
981
|
}
|
863
982
|
if (DEC_ZERO_P(a)) {
|
864
983
|
if (b == DEC_PINF) return x;
|
@@ -868,9 +987,9 @@ dec_divide(int argc, VALUE *argv, VALUE x)
|
|
868
987
|
}
|
869
988
|
if (DEC_ISINF(b)) {
|
870
989
|
if (INUM_NEGATIVE_P(a->inum) == (b == DEC_NINF)) {
|
871
|
-
return dec_pzero();
|
990
|
+
return dec_pzero(0); /* FIXME for scaling */
|
872
991
|
}
|
873
|
-
return dec_nzero();
|
992
|
+
return dec_nzero(0); /* FIXME for scaling */
|
874
993
|
}
|
875
994
|
if (DEC_ZERO_P(b)) {
|
876
995
|
if (INUM_NEGATIVE_P(a->inum) == (b->inum == DEC_NZERO)) {
|
@@ -882,6 +1001,7 @@ dec_divide(int argc, VALUE *argv, VALUE x)
|
|
882
1001
|
}
|
883
1002
|
|
884
1003
|
#ifdef DEBUG
|
1004
|
+
/* :nodoc: */
|
885
1005
|
static VALUE
|
886
1006
|
dec_div(VALUE x, VALUE y)
|
887
1007
|
{
|
@@ -889,75 +1009,72 @@ dec_div(VALUE x, VALUE y)
|
|
889
1009
|
}
|
890
1010
|
#endif
|
891
1011
|
|
892
|
-
/*
|
893
|
-
* FIXME: test needed!
|
894
|
-
*/
|
1012
|
+
/* never accepts NaN for a and b */
|
895
1013
|
static void
|
896
|
-
divmod(Decimal *a, Decimal *b, VALUE *divp, VALUE *modp)
|
1014
|
+
divmod(const Decimal *a, const Decimal *b, VALUE *divp, VALUE *modp)
|
897
1015
|
{
|
898
|
-
|
899
|
-
Decimal *
|
1016
|
+
VALUE div;
|
1017
|
+
Decimal *mod;
|
900
1018
|
|
901
|
-
if (a
|
902
|
-
(
|
903
|
-
|
1019
|
+
if (DEC_ISINF(a) || (!DEC_ISINF(b) && DEC_ZERO_P(b))) {
|
1020
|
+
if (divp) *divp = VALUE_NaN;
|
1021
|
+
if (modp) *modp = VALUE_NaN;
|
1022
|
+
return;
|
904
1023
|
}
|
905
1024
|
else if (INUM_SPZERO_P(a->inum)) {
|
906
|
-
div =
|
907
|
-
div->scale = 0;
|
908
|
-
if (b == DEC_NINF || (b != DEC_PINF && INUM_NEGATIVE_P(b->inum))) {
|
909
|
-
div->inum = DEC_NZERO;
|
910
|
-
}
|
911
|
-
else {
|
912
|
-
div->inum = DEC_PZERO;
|
913
|
-
}
|
1025
|
+
div = INT2FIX(0);
|
914
1026
|
mod = finite_dup(a);
|
915
1027
|
}
|
916
1028
|
else if (DEC_ISINF(b)) {
|
917
1029
|
const int a_negative = INUM_NEGATIVE_P(a->inum);
|
1030
|
+
VALUE div_inum;
|
918
1031
|
|
919
|
-
div = ALLOC(Decimal);
|
920
|
-
div->scale = 0;
|
921
1032
|
if (a_negative != (b == DEC_NINF)) { /* signs differ */
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
else {
|
926
|
-
div->inum = a_negative ? DEC_NZERO : DEC_PZERO;
|
927
|
-
mod = finite_dup(a);
|
1033
|
+
if (divp) *divp = INT2FIX(-1);
|
1034
|
+
if (modp) *modp = b == DEC_PINF ? VALUE_PINF : VALUE_NINF;
|
1035
|
+
return;
|
928
1036
|
}
|
1037
|
+
div = INT2FIX(0);
|
1038
|
+
mod = finite_dup(a);
|
929
1039
|
}
|
930
1040
|
else {
|
931
1041
|
/* both of a and b are finite and nonzero */
|
932
|
-
|
933
|
-
|
934
|
-
|
1042
|
+
Decimal *ddiv = normal_divide(a, b, 0, ROUND_DOWN); /* div = x / y */
|
1043
|
+
Decimal *tmp;
|
1044
|
+
|
1045
|
+
if (INUM_SPZERO_P(ddiv->inum)) {
|
1046
|
+
div = INT2FIX(0);
|
935
1047
|
mod = finite_dup(a);
|
936
1048
|
}
|
937
1049
|
else {
|
938
|
-
|
939
|
-
|
1050
|
+
div = FIXNUM_P(ddiv->inum) ? ddiv->inum : rb_big_clone(ddiv->inum);
|
1051
|
+
tmp = normal_mul(ddiv, b);
|
1052
|
+
mod = normal_plus(a, tmp, Qfalse); /* mod = x - div*y; */
|
940
1053
|
xfree(tmp);
|
941
1054
|
}
|
1055
|
+
xfree(ddiv);
|
942
1056
|
/* if ((mod < 0 && y > 0) || (mod > 0 && y < 0)) { */
|
943
|
-
if (
|
944
|
-
|
945
|
-
|
946
|
-
|
1057
|
+
if (!INUM_SPZERO_P(mod->inum) && !INUM_SPZERO_P(b->inum) &&
|
1058
|
+
INUM_NEGATIVE_P(mod->inum) != INUM_NEGATIVE_P(b->inum)) {
|
1059
|
+
tmp = mod;
|
1060
|
+
mod = normal_plus(tmp, b, Qtrue); /* mod += y; */
|
1061
|
+
xfree(tmp);
|
1062
|
+
INUM_DEC(div); /* div -= 1; */
|
947
1063
|
}
|
948
1064
|
}
|
949
|
-
if (divp) *divp =
|
950
|
-
else if (!DEC_IMMEDIATE_P(div)) xfree(div);
|
1065
|
+
if (divp) *divp = div;
|
951
1066
|
if (modp) *modp = WrapDecimal(mod);
|
952
1067
|
else if (!DEC_IMMEDIATE_P(mod)) xfree(mod);
|
953
1068
|
}
|
954
1069
|
|
1070
|
+
/* :nodoc: */
|
955
1071
|
static VALUE
|
956
1072
|
dec_idiv(VALUE x, VALUE y)
|
957
1073
|
{
|
958
1074
|
Decimal *a, *b;
|
959
1075
|
VALUE div;
|
960
1076
|
|
1077
|
+
CHECK_NAN2(x, y);
|
961
1078
|
switch (TYPE(y)) {
|
962
1079
|
case T_FIXNUM:
|
963
1080
|
case T_BIGNUM:
|
@@ -972,19 +1089,30 @@ dec_idiv(VALUE x, VALUE y)
|
|
972
1089
|
}
|
973
1090
|
/* fall through */
|
974
1091
|
default:
|
975
|
-
return rb_num_coerce_bin(x, y);
|
1092
|
+
return rb_num_coerce_bin(x, y, rb_intern("div"));
|
976
1093
|
}
|
977
1094
|
GetDecimal(x, a);
|
978
1095
|
divmod(a, b, &div, NULL);
|
979
1096
|
return div;
|
980
1097
|
}
|
981
1098
|
|
1099
|
+
/*
|
1100
|
+
* call-seq:
|
1101
|
+
* dec % other => decimal
|
1102
|
+
* dec.modulo(other) => decimal
|
1103
|
+
*
|
1104
|
+
* Return the modulo after division of _dec_ by _other_.
|
1105
|
+
*
|
1106
|
+
* Decimal("6543.21").modulo(137) #=> Decimal(104.21)
|
1107
|
+
* Decimal("6543.21").modulo(Decimal("137.24")) #=> Decimal(92.9299999999996)
|
1108
|
+
*/
|
982
1109
|
static VALUE
|
983
1110
|
dec_mod(VALUE x, VALUE y)
|
984
1111
|
{
|
985
1112
|
Decimal *a, *b;
|
986
1113
|
VALUE mod;
|
987
1114
|
|
1115
|
+
CHECK_NAN2(x, y);
|
988
1116
|
switch (TYPE(y)) {
|
989
1117
|
case T_FIXNUM:
|
990
1118
|
case T_BIGNUM:
|
@@ -999,19 +1127,35 @@ dec_mod(VALUE x, VALUE y)
|
|
999
1127
|
}
|
1000
1128
|
/* fall through */
|
1001
1129
|
default:
|
1002
|
-
return rb_num_coerce_bin(x, y);
|
1130
|
+
return rb_num_coerce_bin(x, y, '%');
|
1003
1131
|
}
|
1004
1132
|
GetDecimal(x, a);
|
1005
1133
|
divmod(a, b, NULL, &mod);
|
1006
1134
|
return mod;
|
1007
1135
|
}
|
1008
1136
|
|
1137
|
+
/*
|
1138
|
+
* call-seq:
|
1139
|
+
* dec.divmod(other) => array
|
1140
|
+
*
|
1141
|
+
* Returns an array containing the quotient and modulus obtained by
|
1142
|
+
* dividing _dec_ by _other_.
|
1143
|
+
*
|
1144
|
+
* Decimal(11).divmod(3) #=> [3, Decimal(2)]
|
1145
|
+
* Decimal(11).divmod(-3) #=> [-4, Decimal(-1)]
|
1146
|
+
* Decimal(11).divmod(Decimal("3.5")) #=> [3, Decimal(0.5)]
|
1147
|
+
* Decimal(-11).divmod(Decimal("3.5")) #=> [-4, Decimal(3.0)]
|
1148
|
+
* Decimal("11.5").divmod(Decimal("3.5")) #=> [3, Decimal(1.0)]
|
1149
|
+
*
|
1150
|
+
* See Numeric#divmod for more details.
|
1151
|
+
*/
|
1009
1152
|
static VALUE
|
1010
1153
|
dec_divmod(VALUE x, VALUE y)
|
1011
1154
|
{
|
1012
1155
|
Decimal *a, *b;
|
1013
1156
|
VALUE div, mod;
|
1014
1157
|
|
1158
|
+
CHECK_NAN2_WITH_VAL(x, y, rb_assoc_new(VALUE_NaN, VALUE_NaN));
|
1015
1159
|
switch (TYPE(y)) {
|
1016
1160
|
case T_FIXNUM:
|
1017
1161
|
case T_BIGNUM:
|
@@ -1026,137 +1170,150 @@ dec_divmod(VALUE x, VALUE y)
|
|
1026
1170
|
}
|
1027
1171
|
/* fall through */
|
1028
1172
|
default:
|
1029
|
-
return rb_num_coerce_bin(x, y);
|
1173
|
+
return rb_num_coerce_bin(x, y, rb_intern("divmod"));
|
1030
1174
|
}
|
1031
1175
|
GetDecimal(x, a);
|
1032
1176
|
divmod(a, b, &div, &mod);
|
1033
1177
|
return rb_assoc_new(div, mod);
|
1034
1178
|
}
|
1035
1179
|
|
1036
|
-
/* XXX: may have bugs; try "GC.stress = true" to reproduce */
|
1037
|
-
/* TODO: can be optimized with removing sign "flip flap?" */
|
1038
1180
|
static VALUE
|
1039
|
-
|
1181
|
+
power_with_fixnum(const Decimal *x, VALUE y)
|
1040
1182
|
{
|
1041
|
-
|
1183
|
+
VALUE inum;
|
1042
1184
|
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
x = normal_mul(x, x);
|
1049
|
-
if (!FIXNUM_P(x->inum)) rb_gc_mark(x->inum);
|
1050
|
-
}
|
1051
|
-
tmp = z;
|
1052
|
-
z = normal_mul(z, x);
|
1053
|
-
if (!FIXNUM_P(z->inum)) rb_gc_mark(z->inum);
|
1054
|
-
if (tmp != orig_x)
|
1055
|
-
xfree(tmp);
|
1056
|
-
}
|
1057
|
-
return WrapDecimal(z);
|
1185
|
+
/* XXX: valid to rb_warn() out of here by rb_big_pow()? */
|
1186
|
+
inum = INUM_POW(x->inum, y);
|
1187
|
+
if (TYPE(inum) == T_FLOAT) /* got Infinity with warning, by too-big y */
|
1188
|
+
return VALUE_PINF;
|
1189
|
+
return WrapDecimal(dec_raw_new(inum, x->scale * FIX2LONG(y)));
|
1058
1190
|
}
|
1059
1191
|
|
1192
|
+
/* TODO: implement dec ** otherdec */
|
1193
|
+
/*
|
1194
|
+
* call-seq:
|
1195
|
+
* dec ** fix => decimal
|
1196
|
+
*
|
1197
|
+
* *WARNING*: The behavior of this method may change.
|
1198
|
+
*
|
1199
|
+
* Raises _dec_ the _fix_ power.
|
1200
|
+
*/
|
1060
1201
|
static VALUE
|
1061
1202
|
dec_pow(VALUE x, VALUE y)
|
1062
1203
|
{
|
1063
1204
|
Decimal *a;
|
1064
1205
|
long l;
|
1065
1206
|
|
1066
|
-
|
1207
|
+
CHECK_NAN(x);
|
1067
1208
|
Check_Type(y, T_FIXNUM);
|
1068
1209
|
l = FIX2LONG(y);
|
1069
1210
|
if (l < 0) rb_raise(rb_eArgError, "in a**b, b should be positive integer");
|
1070
|
-
if (l == 0)
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
return
|
1076
|
-
}
|
1077
|
-
|
1078
|
-
if (a
|
1079
|
-
if (a
|
1080
|
-
|
1081
|
-
return a == DEC_NINF ? VALUE_PINF : dec_uminus(x);
|
1082
|
-
}
|
1083
|
-
return x;
|
1211
|
+
if (l == 0) return WrapDecimal(dec_raw_new(INT2FIX(1), 0));
|
1212
|
+
if (l == 1) return x;
|
1213
|
+
|
1214
|
+
if (x == VALUE_PINF) return x;
|
1215
|
+
if (x == VALUE_NINF) {
|
1216
|
+
return l % 2 == 0 ? VALUE_PINF : VALUE_NINF;
|
1217
|
+
}
|
1218
|
+
GetDecimal(x, a);
|
1219
|
+
if (a->inum == DEC_PZERO) return x;
|
1220
|
+
if (a->inum == DEC_NZERO) {
|
1221
|
+
return l % 2 == 0 ? dec_uminus(x) : x;
|
1084
1222
|
}
|
1085
|
-
return
|
1223
|
+
return power_with_fixnum(a, y);
|
1086
1224
|
}
|
1087
1225
|
|
1088
1226
|
static int
|
1089
|
-
normal_cmp(Decimal *x, Decimal *y)
|
1227
|
+
normal_cmp(const Decimal *x, const Decimal *y)
|
1090
1228
|
{
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1229
|
+
if (INUM_NEGATIVE_P(x->inum) && !INUM_NEGATIVE_P(y->inum)) {
|
1230
|
+
return -1;
|
1231
|
+
}
|
1232
|
+
if (!INUM_NEGATIVE_P(x->inum) && INUM_NEGATIVE_P(y->inum)) {
|
1233
|
+
return 1;
|
1234
|
+
}
|
1235
|
+
if (x->scale == y->scale) return FIX2INT(INUM_CMP(x->inum, y->inum));
|
1236
|
+
/* XXX: can be optimized with INUM_EQ()? */
|
1237
|
+
if (x->scale < y->scale) {
|
1238
|
+
VALUE x_scaled = inum_lshift(x->inum, y->scale - x->scale);
|
1239
|
+
return FIX2INT(INUM_CMP(x_scaled, y->inum));
|
1240
|
+
}
|
1241
|
+
else {
|
1242
|
+
VALUE y_scaled = inum_lshift(y->inum, x->scale - y->scale);
|
1243
|
+
return FIX2INT(INUM_CMP(x->inum, y_scaled));
|
1098
1244
|
}
|
1099
|
-
if (c < 0 && x->scale > y->scale) return -1;
|
1100
|
-
if (c > 0 && x->scale < y->scale) return 1;
|
1101
|
-
/* XXX: align scales */
|
1102
|
-
if (x->scale < y->scale) min = x, max = y;
|
1103
|
-
else min = y, max = x;
|
1104
|
-
n = inum_lshift(min->inum, max->scale - min->scale);
|
1105
|
-
if (x == max) return FIX2INT(INUM_CMP(max->inum, n));
|
1106
|
-
return FIX2INT(INUM_CMP(n, max->inum));
|
1107
1245
|
}
|
1108
1246
|
|
1109
|
-
/* never accepts NaN for x
|
1247
|
+
/* never accepts NaN for x or y */
|
1110
1248
|
static int
|
1111
|
-
cmp(Decimal *x, Decimal *y)
|
1249
|
+
cmp(const Decimal *x, const Decimal *y)
|
1112
1250
|
{
|
1113
1251
|
if (x == y) return 0;
|
1114
1252
|
if (x == DEC_PINF || y == DEC_NINF) return 1;
|
1115
1253
|
if (x == DEC_NINF || y == DEC_PINF) return -1;
|
1116
1254
|
if (INUM_SPZERO_P(x->inum)) {
|
1117
1255
|
if (INUM_SPZERO_P(y->inum)) return 0;
|
1118
|
-
return INUM_NEGATIVE_P(y->inum) ?
|
1256
|
+
return INUM_NEGATIVE_P(y->inum) ? 1 : -1;
|
1119
1257
|
}
|
1120
1258
|
if (INUM_SPZERO_P(y->inum)) {
|
1121
1259
|
return INUM_NEGATIVE_P(x->inum) ? -1 : 1;
|
1122
1260
|
}
|
1123
|
-
return normal_cmp(x, y);
|
1261
|
+
return normal_cmp(x, y);
|
1124
1262
|
}
|
1125
1263
|
|
1264
|
+
/*
|
1265
|
+
* call-seq:
|
1266
|
+
* dec == other => true or false
|
1267
|
+
*
|
1268
|
+
* Returns +true+ only if _other_ has the same value as _dec_.
|
1269
|
+
* Contrast this with eql?, which requires _other_
|
1270
|
+
* to be the same class, a +Decimal+.
|
1271
|
+
*
|
1272
|
+
* Decimal(1) == 1 #=> true
|
1273
|
+
* Decimal(1) == Decimal("1.0") #=> true
|
1274
|
+
* Decimal(1) == 1.0 #=> false
|
1275
|
+
*/
|
1126
1276
|
static VALUE
|
1127
1277
|
dec_eq(VALUE x, VALUE y)
|
1128
1278
|
{
|
1129
1279
|
Decimal *a, *b;
|
1130
1280
|
|
1281
|
+
CHECK_NAN2_WITH_VAL(x, y, Qfalse);
|
1131
1282
|
GetDecimal(x, a);
|
1132
|
-
if (a == DEC_NaN) return Qfalse;
|
1133
1283
|
switch (TYPE(y)) {
|
1134
1284
|
case T_FIXNUM:
|
1135
1285
|
case T_BIGNUM:
|
1136
1286
|
b = inum_to_dec(y);
|
1137
1287
|
break;
|
1138
1288
|
case T_FLOAT:
|
1139
|
-
return
|
1289
|
+
return Qfalse;
|
1140
1290
|
case T_DATA:
|
1141
1291
|
if (DECIMAL_P(y)) {
|
1142
1292
|
GetDecimal(y, b);
|
1143
|
-
if (b == DEC_NaN) return Qfalse;
|
1144
1293
|
break;
|
1145
1294
|
}
|
1146
1295
|
/* fall through */
|
1147
1296
|
default:
|
1148
|
-
return rb_num_coerce_cmp(x, y);
|
1297
|
+
return RTEST(rb_num_coerce_cmp(x, y, rb_intern("==")));
|
1149
1298
|
}
|
1150
1299
|
return cmp(a, b) == 0 ? Qtrue : Qfalse;
|
1151
1300
|
}
|
1152
1301
|
|
1302
|
+
/*
|
1303
|
+
* call-seq:
|
1304
|
+
* dec <=> other => -1, 0, +1
|
1305
|
+
*
|
1306
|
+
* Returns -1, 0, or +1 depending on whether _dec_ is less than,
|
1307
|
+
* equal to, or greater than _other_. This is the basis for the
|
1308
|
+
* tests in +Comparable+.
|
1309
|
+
*/
|
1153
1310
|
static VALUE
|
1154
1311
|
dec_cmp(VALUE x, VALUE y)
|
1155
1312
|
{
|
1156
1313
|
Decimal *a, *b;
|
1157
1314
|
|
1315
|
+
CHECK_NAN2_WITH_VAL(x, y, Qnil);
|
1158
1316
|
GetDecimal(x, a);
|
1159
|
-
if (a == DEC_NaN) return Qnil;
|
1160
1317
|
switch (TYPE(y)) {
|
1161
1318
|
case T_FIXNUM:
|
1162
1319
|
case T_BIGNUM:
|
@@ -1167,23 +1324,28 @@ dec_cmp(VALUE x, VALUE y)
|
|
1167
1324
|
case T_DATA:
|
1168
1325
|
if (DECIMAL_P(y)) {
|
1169
1326
|
GetDecimal(y, b);
|
1170
|
-
if (b == DEC_NaN) return Qnil;
|
1171
1327
|
break;
|
1172
1328
|
}
|
1173
1329
|
/* fall through */
|
1174
1330
|
default:
|
1175
|
-
return rb_num_coerce_cmp(x, y);
|
1331
|
+
return rb_num_coerce_cmp(x, y, rb_intern("<=>"));
|
1176
1332
|
}
|
1177
1333
|
return INT2FIX(cmp(a, b));
|
1178
1334
|
}
|
1179
1335
|
|
1336
|
+
/*
|
1337
|
+
* call-seq:
|
1338
|
+
* dec > other => true or false
|
1339
|
+
*
|
1340
|
+
* Returns +true+ if _dec_ is greater than _other_.
|
1341
|
+
*/
|
1180
1342
|
static VALUE
|
1181
1343
|
dec_gt(VALUE x, VALUE y)
|
1182
1344
|
{
|
1183
1345
|
Decimal *a, *b;
|
1184
1346
|
|
1347
|
+
CHECK_NAN2_WITH_VAL(x, y, Qfalse);
|
1185
1348
|
GetDecimal(x, a);
|
1186
|
-
if (a == DEC_NaN) return Qfalse;
|
1187
1349
|
switch (TYPE(y)) {
|
1188
1350
|
case T_FIXNUM:
|
1189
1351
|
case T_BIGNUM:
|
@@ -1195,23 +1357,28 @@ dec_gt(VALUE x, VALUE y)
|
|
1195
1357
|
case T_DATA:
|
1196
1358
|
if (DECIMAL_P(y)) {
|
1197
1359
|
GetDecimal(y, b);
|
1198
|
-
if (b == DEC_NaN) return Qfalse;
|
1199
1360
|
break;
|
1200
1361
|
}
|
1201
1362
|
/* fall through */
|
1202
1363
|
default:
|
1203
|
-
return rb_num_coerce_relop(x, y);
|
1364
|
+
return rb_num_coerce_relop(x, y, '>');
|
1204
1365
|
}
|
1205
1366
|
return cmp(a, b) > 0 ? Qtrue : Qfalse;
|
1206
1367
|
}
|
1207
1368
|
|
1369
|
+
/*
|
1370
|
+
* call-seq:
|
1371
|
+
* dec >= other => true or false
|
1372
|
+
*
|
1373
|
+
* Returns +true+ if _dec_ is greater than or equal to _other_.
|
1374
|
+
*/
|
1208
1375
|
static VALUE
|
1209
1376
|
dec_ge(VALUE x, VALUE y)
|
1210
1377
|
{
|
1211
1378
|
Decimal *a, *b;
|
1212
1379
|
|
1380
|
+
CHECK_NAN2_WITH_VAL(x, y, Qfalse);
|
1213
1381
|
GetDecimal(x, a);
|
1214
|
-
if (a == DEC_NaN) return Qfalse;
|
1215
1382
|
switch (TYPE(y)) {
|
1216
1383
|
case T_FIXNUM:
|
1217
1384
|
case T_BIGNUM:
|
@@ -1223,23 +1390,28 @@ dec_ge(VALUE x, VALUE y)
|
|
1223
1390
|
case T_DATA:
|
1224
1391
|
if (DECIMAL_P(y)) {
|
1225
1392
|
GetDecimal(y, b);
|
1226
|
-
if (b == DEC_NaN) return Qfalse;
|
1227
1393
|
break;
|
1228
1394
|
}
|
1229
1395
|
/* fall through */
|
1230
1396
|
default:
|
1231
|
-
return rb_num_coerce_relop(x, y);
|
1397
|
+
return rb_num_coerce_relop(x, y, rb_intern(">="));
|
1232
1398
|
}
|
1233
1399
|
return cmp(a, b) >= 0 ? Qtrue : Qfalse;
|
1234
1400
|
}
|
1235
1401
|
|
1402
|
+
/*
|
1403
|
+
* call-seq:
|
1404
|
+
* dec < other => true or false
|
1405
|
+
*
|
1406
|
+
* Returns +true+ if _dec_ is less than _other_.
|
1407
|
+
*/
|
1236
1408
|
static VALUE
|
1237
1409
|
dec_lt(VALUE x, VALUE y)
|
1238
1410
|
{
|
1239
1411
|
Decimal *a, *b;
|
1240
1412
|
|
1413
|
+
CHECK_NAN2_WITH_VAL(x, y, Qfalse);
|
1241
1414
|
GetDecimal(x, a);
|
1242
|
-
if (a == DEC_NaN) return Qfalse;
|
1243
1415
|
switch (TYPE(y)) {
|
1244
1416
|
case T_FIXNUM:
|
1245
1417
|
case T_BIGNUM:
|
@@ -1251,23 +1423,28 @@ dec_lt(VALUE x, VALUE y)
|
|
1251
1423
|
case T_DATA:
|
1252
1424
|
if (DECIMAL_P(y)) {
|
1253
1425
|
GetDecimal(y, b);
|
1254
|
-
if (b == DEC_NaN) return Qfalse;
|
1255
1426
|
break;
|
1256
1427
|
}
|
1257
1428
|
/* fall through */
|
1258
1429
|
default:
|
1259
|
-
return rb_num_coerce_relop(x, y);
|
1430
|
+
return rb_num_coerce_relop(x, y, '<');
|
1260
1431
|
}
|
1261
1432
|
return cmp(a, b) < 0 ? Qtrue : Qfalse;
|
1262
1433
|
}
|
1263
1434
|
|
1435
|
+
/*
|
1436
|
+
* call-seq:
|
1437
|
+
* dec <= other => true or false
|
1438
|
+
*
|
1439
|
+
* Returns +true+ if _dec_ is less than or equal to _other_.
|
1440
|
+
*/
|
1264
1441
|
static VALUE
|
1265
1442
|
dec_le(VALUE x, VALUE y)
|
1266
1443
|
{
|
1267
1444
|
Decimal *a, *b;
|
1268
1445
|
|
1446
|
+
CHECK_NAN2_WITH_VAL(x, y, Qfalse);
|
1269
1447
|
GetDecimal(x, a);
|
1270
|
-
if (a == DEC_NaN) return Qfalse;
|
1271
1448
|
switch (TYPE(y)) {
|
1272
1449
|
case T_FIXNUM:
|
1273
1450
|
case T_BIGNUM:
|
@@ -1279,16 +1456,27 @@ dec_le(VALUE x, VALUE y)
|
|
1279
1456
|
case T_DATA:
|
1280
1457
|
if (DECIMAL_P(y)) {
|
1281
1458
|
GetDecimal(y, b);
|
1282
|
-
if (b == DEC_NaN) return Qfalse;
|
1283
1459
|
break;
|
1284
1460
|
}
|
1285
1461
|
/* fall through */
|
1286
1462
|
default:
|
1287
|
-
return rb_num_coerce_relop(x, y);
|
1463
|
+
return rb_num_coerce_relop(x, y, rb_intern("<="));
|
1288
1464
|
}
|
1289
1465
|
return cmp(a, b) <= 0 ? Qtrue : Qfalse;
|
1290
1466
|
}
|
1291
1467
|
|
1468
|
+
/*
|
1469
|
+
* call-seq:
|
1470
|
+
* dec.eql?(other) => true or false
|
1471
|
+
*
|
1472
|
+
* Returns +true+ if _other_ is a +Decimal+ and is equal to _dec_
|
1473
|
+
* including their values of scale.
|
1474
|
+
*
|
1475
|
+
* Decimal(1) == 1 #=> true
|
1476
|
+
* Decimal(1).eql?(1) #=> false
|
1477
|
+
* Decimal(1).eql?(Decimal(1)) #=> true
|
1478
|
+
* Decimal(1).eql?(Decimal("1.0"))) #=> false
|
1479
|
+
*/
|
1292
1480
|
static VALUE
|
1293
1481
|
dec_eql(VALUE x, VALUE y)
|
1294
1482
|
{
|
@@ -1297,12 +1485,12 @@ dec_eql(VALUE x, VALUE y)
|
|
1297
1485
|
if (TYPE(y) != T_DATA || !DECIMAL_P(y))
|
1298
1486
|
return Qfalse;
|
1299
1487
|
|
1488
|
+
CHECK_NAN2_WITH_VAL(x, y, Qfalse);
|
1489
|
+
if (DEC_VALUE_ISINF(x) || DEC_VALUE_ISINF(y))
|
1490
|
+
return x == y ? Qtrue : Qfalse;
|
1491
|
+
|
1300
1492
|
GetDecimal(x, a);
|
1301
1493
|
GetDecimal(y, b);
|
1302
|
-
if (a == DEC_NaN || b == DEC_NaN) return Qfalse;
|
1303
|
-
if (DEC_ISINF(a) || DEC_ISINF(b))
|
1304
|
-
return a == b ? Qtrue : Qfalse;
|
1305
|
-
|
1306
1494
|
if (a->scale != b->scale)
|
1307
1495
|
return Qfalse;
|
1308
1496
|
if (a->inum == b->inum)
|
@@ -1314,6 +1502,12 @@ dec_eql(VALUE x, VALUE y)
|
|
1314
1502
|
return Qfalse;
|
1315
1503
|
}
|
1316
1504
|
|
1505
|
+
/*
|
1506
|
+
* call-seq:
|
1507
|
+
* dec.hash => integer
|
1508
|
+
*
|
1509
|
+
* Returns a hash code for _dec_.
|
1510
|
+
*/
|
1317
1511
|
static VALUE
|
1318
1512
|
dec_hash(VALUE x)
|
1319
1513
|
{
|
@@ -1322,93 +1516,156 @@ dec_hash(VALUE x)
|
|
1322
1516
|
|
1323
1517
|
GetDecimal(x, d);
|
1324
1518
|
if (!DEC_IMMEDIATE_P(d)) {
|
1325
|
-
|
1519
|
+
VALUE inum = d->inum;
|
1520
|
+
|
1521
|
+
if (INUM_SPZERO_P(inum)) inum = INT2FIX(0);
|
1522
|
+
hash = NUM2LONG(INUM_HASH(inum));
|
1326
1523
|
hash ^= d->scale;
|
1327
1524
|
}
|
1328
1525
|
else hash = (long)d;
|
1329
1526
|
return LONG2NUM(hash);
|
1330
1527
|
}
|
1331
1528
|
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
stmt; \
|
1338
|
-
ruby_verbose = verbose; \
|
1339
|
-
); \
|
1340
|
-
} while (0)
|
1529
|
+
static Decimal *
|
1530
|
+
dbl_threshold_to_dec(double threshold)
|
1531
|
+
{
|
1532
|
+
VALUE v = flo_to_s(rb_float_new(threshold));
|
1533
|
+
Decimal *d = cstr_to_dec(StringValueCStr(v));
|
1341
1534
|
|
1342
|
-
|
1343
|
-
|
1535
|
+
if (!IMMEDIATE_P(d->inum)) {
|
1536
|
+
rb_global_variable(&d->inum);
|
1537
|
+
}
|
1538
|
+
return d;
|
1539
|
+
}
|
1540
|
+
|
1541
|
+
static VALUE
|
1542
|
+
dbl_threshold_to_inum(double threshold, VALUE *val)
|
1344
1543
|
{
|
1345
|
-
double f;
|
1544
|
+
const double f = floor(threshold);
|
1545
|
+
|
1546
|
+
if (FIXABLE(f)) {
|
1547
|
+
return *val = LONG2FIX((long)f);
|
1548
|
+
}
|
1549
|
+
rb_global_variable(val);
|
1550
|
+
return *val = rb_dbl2big(f);
|
1551
|
+
}
|
1346
1552
|
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1553
|
+
static int
|
1554
|
+
out_of_double_range_p(const Decimal *d, double *f)
|
1555
|
+
{
|
1556
|
+
const Decimal *d_abs;
|
1557
|
+
int negative, out_of_range = Qfalse;
|
1350
1558
|
|
1351
|
-
|
1352
|
-
|
1559
|
+
if (!INUM_NEGATIVE_P(d->inum)) {
|
1560
|
+
negative = Qfalse;
|
1561
|
+
d_abs = d;
|
1353
1562
|
}
|
1354
1563
|
else {
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1564
|
+
negative = Qtrue;
|
1565
|
+
d_abs = dec_raw_new(INUM_UMINUS(d->inum), d->scale);
|
1566
|
+
}
|
1358
1567
|
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
f /= divf;
|
1568
|
+
if (normal_cmp(d_abs, GET_DEC_DBL_MIN()) < 0) { /* too small */
|
1569
|
+
*f = negative ? -0.0 : 0.0;
|
1570
|
+
out_of_range = Qtrue;
|
1363
1571
|
}
|
1364
|
-
if (
|
1365
|
-
|
1366
|
-
|
1572
|
+
else if (normal_cmp(d_abs, GET_DEC_DBL_MAX()) > 0) { /* too big */
|
1573
|
+
*f = negative ? -INFINITY : INFINITY;
|
1574
|
+
out_of_range = Qtrue;
|
1575
|
+
}
|
1576
|
+
if (d_abs != d) xfree((void *)d_abs);
|
1577
|
+
return out_of_range;
|
1578
|
+
}
|
1579
|
+
|
1580
|
+
static double
|
1581
|
+
normal_to_f(const Decimal *d)
|
1582
|
+
{
|
1583
|
+
double f;
|
1584
|
+
|
1585
|
+
if (d->scale <= 0) {
|
1586
|
+
f = NUM2DBL(d->inum) * pow(10.0, -d->scale);
|
1587
|
+
}
|
1588
|
+
else { /* NUM2DBL() may warn */
|
1589
|
+
const int negative = INUM_NEGATIVE_P(d->inum);
|
1590
|
+
long scale = d->scale;
|
1591
|
+
VALUE inum_abs = negative ? INUM_UMINUS(d->inum) : d->inum;
|
1592
|
+
|
1593
|
+
while (INUM_GT(inum_abs, GET_INUM_DBL_MAX())) {
|
1594
|
+
inum_abs = INUM_DIV(inum_abs, INT2FIX(10));
|
1595
|
+
scale--;
|
1596
|
+
}
|
1597
|
+
f = NUM2DBL(inum_abs) / pow(10.0, scale); /* scale may be negative */
|
1598
|
+
if (negative) f = -f;
|
1367
1599
|
}
|
1368
1600
|
return f;
|
1369
1601
|
}
|
1370
1602
|
|
1603
|
+
/*
|
1604
|
+
* call-seq:
|
1605
|
+
* dec.to_f => float
|
1606
|
+
*
|
1607
|
+
* Converts _dec_ to a +Float+. Note that this may lose some precision
|
1608
|
+
* and/or exactness.
|
1609
|
+
* If you want to operate +Decimal+ with +Float+, use this method explicitly.
|
1610
|
+
*/
|
1371
1611
|
static VALUE
|
1372
1612
|
dec_to_f(VALUE num)
|
1373
1613
|
{
|
1374
1614
|
Decimal *d;
|
1375
1615
|
double f;
|
1376
1616
|
|
1617
|
+
CHECK_NAN_WITH_VAL(num, rb_float_new(NAN));
|
1618
|
+
if (num == VALUE_PINF)
|
1619
|
+
return rb_float_new(INFINITY);
|
1620
|
+
if (num == VALUE_NINF)
|
1621
|
+
return rb_float_new(-INFINITY);
|
1622
|
+
|
1377
1623
|
GetDecimal(num, d);
|
1378
|
-
if (d ==
|
1379
|
-
f = 0.0 / 0.0;
|
1380
|
-
else if (d == DEC_PINF)
|
1381
|
-
f = 1.0 / 0.0;
|
1382
|
-
else if (d == DEC_NINF)
|
1383
|
-
f = -1.0 / 0.0;
|
1384
|
-
else if (d->inum == DEC_PZERO)
|
1624
|
+
if (d->inum == DEC_PZERO)
|
1385
1625
|
f = 0.0;
|
1386
1626
|
else if (d->inum == DEC_NZERO)
|
1387
1627
|
f = -0.0;
|
1628
|
+
else if (out_of_double_range_p(d, &f))
|
1629
|
+
rb_warning("Decimal out of Float range");
|
1388
1630
|
else
|
1389
1631
|
f = normal_to_f(d);
|
1390
1632
|
|
1391
1633
|
return rb_float_new(f);
|
1392
1634
|
}
|
1393
1635
|
|
1636
|
+
/*
|
1637
|
+
* call-seq:
|
1638
|
+
* dec.abs => decimal
|
1639
|
+
*
|
1640
|
+
* Returns the absolute value of _dec_.
|
1641
|
+
*
|
1642
|
+
* Decimal("34.56").abs #=> Decimal(34.56)
|
1643
|
+
* Decimal("-34.56").abs #=> Decimal(34.56)
|
1644
|
+
*/
|
1394
1645
|
static VALUE
|
1395
1646
|
dec_abs(VALUE num)
|
1396
1647
|
{
|
1397
|
-
Decimal *d
|
1648
|
+
Decimal *d;
|
1649
|
+
VALUE inum;
|
1398
1650
|
|
1399
|
-
|
1400
|
-
if (
|
1651
|
+
CHECK_NAN(num);
|
1652
|
+
if (DEC_VALUE_ISINF(num))
|
1401
1653
|
return VALUE_PINF;
|
1402
|
-
|
1403
|
-
|
1654
|
+
GetDecimal(num, d);
|
1655
|
+
if (d->inum == DEC_PZERO ||
|
1656
|
+
(d->inum != DEC_NZERO && !INUM_NEGATIVE_P(d->inum))) {
|
1404
1657
|
return num;
|
1405
1658
|
}
|
1406
|
-
|
1407
|
-
|
1408
|
-
d2->scale = d->scale;
|
1409
|
-
return WrapDecimal(d2);
|
1659
|
+
inum = (d->inum == DEC_NZERO) ? DEC_PZERO : INUM_UMINUS(d->inum);
|
1660
|
+
return WrapDecimal(dec_raw_new(inum, d->scale));
|
1410
1661
|
}
|
1411
1662
|
|
1663
|
+
/*
|
1664
|
+
* call-seq:
|
1665
|
+
* dec.zero? => true or false
|
1666
|
+
*
|
1667
|
+
* Returns +true+ if _dec_ is zero.
|
1668
|
+
*/
|
1412
1669
|
static VALUE
|
1413
1670
|
dec_zero_p(VALUE num)
|
1414
1671
|
{
|
@@ -1421,6 +1678,12 @@ dec_zero_p(VALUE num)
|
|
1421
1678
|
return Qfalse;
|
1422
1679
|
}
|
1423
1680
|
|
1681
|
+
/*
|
1682
|
+
* call-seq:
|
1683
|
+
* dec.to_i => integer
|
1684
|
+
*
|
1685
|
+
* Returns _dec_ truncated to an +Integer+.
|
1686
|
+
*/
|
1424
1687
|
static VALUE
|
1425
1688
|
dec_to_i(VALUE num)
|
1426
1689
|
{
|
@@ -1428,113 +1691,315 @@ dec_to_i(VALUE num)
|
|
1428
1691
|
VALUE inum;
|
1429
1692
|
|
1430
1693
|
GetDecimal(num, d);
|
1431
|
-
do_round(d, 0, ROUND_DOWN, &inum); /*
|
1694
|
+
do_round(d, 0, ROUND_DOWN, &inum); /* equal to "d.round(0, :down)" */
|
1432
1695
|
return inum;
|
1433
|
-
}
|
1696
|
+
}
|
1434
1697
|
|
1435
1698
|
static VALUE
|
1436
1699
|
rounding_method(int argc, VALUE *argv, VALUE x, VALUE mode)
|
1437
1700
|
{
|
1438
1701
|
Decimal *d;
|
1439
|
-
VALUE
|
1702
|
+
VALUE vscale, inum;
|
1703
|
+
long scale = 0;
|
1440
1704
|
|
1441
|
-
rb_scan_args(argc, argv, "01", &
|
1705
|
+
rb_scan_args(argc, argv, "01", &vscale);
|
1442
1706
|
GetDecimal(x, d);
|
1443
|
-
if (argc ==
|
1444
|
-
|
1707
|
+
if (argc == 1) scale = NUM2LONG(vscale);
|
1708
|
+
if (scale <= 0) {
|
1709
|
+
do_round(d, scale, mode, &inum);
|
1445
1710
|
return inum;
|
1446
1711
|
}
|
1447
|
-
return WrapDecimal(do_round(d,
|
1712
|
+
return WrapDecimal(do_round(d, scale, mode, NULL));
|
1448
1713
|
}
|
1449
1714
|
|
1715
|
+
/*
|
1716
|
+
* call-seq:
|
1717
|
+
* dec.truncate(n=0) => integer or decimal
|
1718
|
+
*
|
1719
|
+
* Returns _dec_ truncated to an +Integer+.
|
1720
|
+
*
|
1721
|
+
* This is identical to <code>dec.round(n, Decimal::ROUND_DOWN)</code>.
|
1722
|
+
* See <code>Decimal#round</code> for more details.
|
1723
|
+
*/
|
1450
1724
|
static VALUE
|
1451
1725
|
dec_truncate(int argc, VALUE *argv, VALUE x)
|
1452
1726
|
{
|
1453
1727
|
return rounding_method(argc, argv, x, ROUND_DOWN);
|
1454
1728
|
}
|
1455
1729
|
|
1730
|
+
/*
|
1731
|
+
* call-seq:
|
1732
|
+
* dec.floor(n=0) => integer or decimal
|
1733
|
+
*
|
1734
|
+
* Returns the largest integer less than or equal to _dec_.
|
1735
|
+
*
|
1736
|
+
* Decimal("1.2").floor #=> 1
|
1737
|
+
* Decimal("2.0").floor #=> 2
|
1738
|
+
* Decimal("-1.2").floor #=> -2
|
1739
|
+
* Decimal("-2.0").floor #=> -2
|
1740
|
+
*
|
1741
|
+
* This is identical to <code>dec.round(n, Decimal::ROUND_FLOOR)</code>.
|
1742
|
+
* See <code>Decimal#round</code> for more details.
|
1743
|
+
*/
|
1456
1744
|
static VALUE
|
1457
1745
|
dec_floor(int argc, VALUE *argv, VALUE x)
|
1458
1746
|
{
|
1459
1747
|
return rounding_method(argc, argv, x, ROUND_FLOOR);
|
1460
1748
|
}
|
1461
1749
|
|
1750
|
+
/*
|
1751
|
+
* call-seq:
|
1752
|
+
* dec.ceil(n=0) => integer or decimal
|
1753
|
+
*
|
1754
|
+
* Returns the smallest +Integer+ greater than or equal to _dec_.
|
1755
|
+
*
|
1756
|
+
* Decimal("1.2").ceil #=> 2
|
1757
|
+
* Decimal("2.0").ceil #=> 2
|
1758
|
+
* Decimal("-1.2").ceil #=> -1
|
1759
|
+
* Decimal("-2.0").ceil #=> -2
|
1760
|
+
*
|
1761
|
+
* This is identical to <code>dec.round(n, Decimal::ROUND_CEILING)</code>.
|
1762
|
+
* See <code>Decimal#round</code> for more details.
|
1763
|
+
*/
|
1462
1764
|
static VALUE
|
1463
1765
|
dec_ceil(int argc, VALUE *argv, VALUE x)
|
1464
1766
|
{
|
1465
1767
|
return rounding_method(argc, argv, x, ROUND_CEILING);
|
1466
1768
|
}
|
1467
1769
|
|
1770
|
+
/*
|
1771
|
+
* call-seq:
|
1772
|
+
* dec.round(n=0, mode=Decimal::ROUND_HALF_UP) => integer or decimal
|
1773
|
+
*
|
1774
|
+
* *FIXME*: more examples
|
1775
|
+
*
|
1776
|
+
* Rounds _dec_ to a given precision _n_ in decimal digits (default 0 digits)
|
1777
|
+
* with rounding mode _mode_. Precision may be negative. Returns a
|
1778
|
+
* +Decimal+ when _n_ is greater than 0, +Integer+ otherwise.
|
1779
|
+
*
|
1780
|
+
* Decimal("1.5").round #=> 2
|
1781
|
+
* Decimal("-1.5").round #=> -2
|
1782
|
+
*/
|
1468
1783
|
static VALUE
|
1469
1784
|
dec_round(int argc, VALUE *argv, VALUE x)
|
1470
1785
|
{
|
1471
1786
|
Decimal *d;
|
1472
|
-
VALUE vscale;
|
1787
|
+
VALUE vscale, mode;
|
1473
1788
|
long scale = 0;
|
1474
|
-
VALUE mode = ROUND_HALF_UP, tmpmode;
|
1475
1789
|
|
1476
|
-
rb_scan_args(argc, argv, "02", &vscale, &
|
1790
|
+
rb_scan_args(argc, argv, "02", &vscale, &mode);
|
1477
1791
|
switch (argc) {
|
1478
1792
|
case 2:
|
1479
1793
|
Check_Type(mode, T_SYMBOL);
|
1480
|
-
if (!
|
1794
|
+
if (!valid_rounding_mode_p(mode)) {
|
1481
1795
|
rb_raise(rb_eArgError, "invalid rounding mode %s",
|
1482
|
-
|
1796
|
+
RSTRING_PTR(rb_inspect(mode)));
|
1483
1797
|
}
|
1484
|
-
mode = tmpmode;
|
1485
1798
|
/* fall through */
|
1486
1799
|
case 1:
|
1487
1800
|
scale = NUM2LONG(vscale);
|
1488
1801
|
/* fall through */
|
1489
1802
|
default:
|
1803
|
+
if (NIL_P(mode)) mode = ROUND_HALF_UP;
|
1490
1804
|
break;
|
1491
1805
|
}
|
1492
1806
|
GetDecimal(x, d);
|
1493
|
-
if (scale
|
1807
|
+
if (scale <= 0) {
|
1494
1808
|
VALUE inum;
|
1495
1809
|
|
1496
1810
|
do_round(d, scale, mode, &inum);
|
1497
1811
|
return inum;
|
1498
1812
|
}
|
1499
|
-
return WrapDecimal(do_round(d, scale, mode,
|
1813
|
+
return WrapDecimal(do_round(d, scale, mode, NULL));
|
1500
1814
|
}
|
1501
1815
|
|
1502
1816
|
|
1817
|
+
/*
|
1818
|
+
* call-seq:
|
1819
|
+
* dec.nan? => true or false
|
1820
|
+
*
|
1821
|
+
* Returns +true+ if _dec_ is an invalid point number, NaN.
|
1822
|
+
*
|
1823
|
+
* Decimal(-1).nan? #=> false
|
1824
|
+
* Decimal(1).divide(0).nan? #=> false
|
1825
|
+
* Decimal(0).divide(0).nan? #=> true
|
1826
|
+
*/
|
1503
1827
|
static VALUE
|
1504
1828
|
dec_nan_p(VALUE num)
|
1505
1829
|
{
|
1506
|
-
|
1507
|
-
|
1508
|
-
GetDecimal(num, d);
|
1509
|
-
return d == DEC_NaN ? Qtrue : Qfalse;
|
1830
|
+
return num == VALUE_NaN;
|
1510
1831
|
}
|
1511
1832
|
|
1833
|
+
/*
|
1834
|
+
* call-seq:
|
1835
|
+
* dec.finite? => true or false
|
1836
|
+
*
|
1837
|
+
* Returns +true+ if _dec_ is a finite number (it is not infinite
|
1838
|
+
* nor NaN).
|
1839
|
+
*
|
1840
|
+
* Decimal(0).finite? #=> true
|
1841
|
+
* Decimal(1).divide(0).finite? #=> false
|
1842
|
+
* Decimal(0).divide(0).finite? #=> false
|
1843
|
+
*/
|
1512
1844
|
static VALUE
|
1513
1845
|
dec_finite_p(VALUE num)
|
1514
1846
|
{
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
return
|
1847
|
+
if (!DEC_VALUE_ISINF(num) && num != VALUE_NaN) {
|
1848
|
+
return Qtrue;
|
1849
|
+
}
|
1850
|
+
return Qfalse;
|
1519
1851
|
}
|
1520
1852
|
|
1853
|
+
/*
|
1854
|
+
* call-seq:
|
1855
|
+
* dec.infinite? => nil, -1, +1
|
1856
|
+
*
|
1857
|
+
* Returns +nil+, -1, or +1 depending on whether _dec_ is finite,
|
1858
|
+
* -infinity, or +infinity.
|
1859
|
+
*
|
1860
|
+
* Decimal(0).infinite? #=> nil
|
1861
|
+
* Decimal(-1).divide(0).infinite? #=> -1
|
1862
|
+
* Decimal(+1).divide(0).infinite? #=> 1
|
1863
|
+
*/
|
1521
1864
|
static VALUE
|
1522
1865
|
dec_infinite_p(VALUE num)
|
1523
1866
|
{
|
1524
|
-
|
1525
|
-
|
1526
|
-
GetDecimal(num, d);
|
1527
|
-
if (d == DEC_PINF) return INT2FIX(1);
|
1528
|
-
if (d == DEC_NINF) return INT2FIX(-1);
|
1867
|
+
if (num == VALUE_PINF) return INT2FIX(1);
|
1868
|
+
if (num == VALUE_NINF) return INT2FIX(-1);
|
1529
1869
|
return Qnil;
|
1530
1870
|
}
|
1531
1871
|
|
1872
|
+
|
1873
|
+
/*
|
1874
|
+
* Mathematical part
|
1875
|
+
*/
|
1876
|
+
static VALUE mMath;
|
1877
|
+
|
1878
|
+
/* :nodoc: */
|
1879
|
+
static VALUE
|
1880
|
+
math_ldexp10(VALUE module UNUSED, VALUE x, VALUE exp)
|
1881
|
+
{
|
1882
|
+
int integer;
|
1883
|
+
long lexp;
|
1884
|
+
Decimal *d, *d2;
|
1885
|
+
|
1886
|
+
lexp = NUM2LONG(exp);
|
1887
|
+
if (lexp == 0)
|
1888
|
+
return x;
|
1889
|
+
CHECK_NAN(x);
|
1890
|
+
if (DEC_VALUE_ISINF(x))
|
1891
|
+
return x;
|
1892
|
+
|
1893
|
+
if (TYPE(x) == T_FIXNUM || TYPE(x) == T_BIGNUM) {
|
1894
|
+
integer = Qtrue;
|
1895
|
+
d = inum_to_dec(x);
|
1896
|
+
}
|
1897
|
+
else {
|
1898
|
+
integer = Qfalse;
|
1899
|
+
GetDecimal(x, d);
|
1900
|
+
}
|
1901
|
+
if (lexp > 0 && LONG_MIN + lexp > d->scale)
|
1902
|
+
rb_raise(rb_eArgError, "%ld is too big", lexp);
|
1903
|
+
if (lexp < 0 && LONG_MAX + lexp < d->scale)
|
1904
|
+
rb_raise(rb_eArgError, "%ld is too small", lexp);
|
1905
|
+
d2 = finite_dup(d);
|
1906
|
+
d2->scale = d->scale - lexp;
|
1907
|
+
if (integer) xfree(d);
|
1908
|
+
return WrapDecimal(d2);
|
1909
|
+
}
|
1910
|
+
|
1911
|
+
/* :nodoc: */
|
1912
|
+
static VALUE
|
1913
|
+
math_frexp10(VALUE module UNUSED, VALUE x)
|
1914
|
+
{
|
1915
|
+
int negative, integer;
|
1916
|
+
long exp;
|
1917
|
+
VALUE inum;
|
1918
|
+
Decimal *d, *mant;
|
1919
|
+
static const Decimal *min = NULL /* to be 0.1 */, *max; /* to be 1 */
|
1920
|
+
|
1921
|
+
CHECK_NAN_WITH_VAL(x, rb_assoc_new(VALUE_NaN, INT2FIX(0)));
|
1922
|
+
if (DEC_VALUE_ISINF(x))
|
1923
|
+
return rb_assoc_new(x, INT2FIX(0));
|
1924
|
+
|
1925
|
+
if (TYPE(x) == T_FIXNUM || TYPE(x) == T_BIGNUM) {
|
1926
|
+
integer = Qtrue;
|
1927
|
+
d = inum_to_dec(x);
|
1928
|
+
}
|
1929
|
+
else {
|
1930
|
+
integer = Qfalse;
|
1931
|
+
GetDecimal(x, d);
|
1932
|
+
}
|
1933
|
+
if (DEC_ZERO_P(d)) {
|
1934
|
+
if (integer) xfree(d);
|
1935
|
+
return rb_assoc_new(x, INT2FIX(0));
|
1936
|
+
}
|
1937
|
+
|
1938
|
+
if (INUM_NEGATIVE_P(d->inum)) {
|
1939
|
+
negative = Qtrue;
|
1940
|
+
inum = INUM_UMINUS(d->inum);
|
1941
|
+
} else {
|
1942
|
+
negative = Qfalse;
|
1943
|
+
inum = d->inum;
|
1944
|
+
}
|
1945
|
+
mant = dec_raw_new(inum, d->scale);
|
1946
|
+
exp = 0;
|
1947
|
+
if (min == NULL) {
|
1948
|
+
min = dec_raw_new(INT2FIX(1), 1); /* 0.1 */
|
1949
|
+
max = dec_raw_new(INT2FIX(1), 0); /* 1 */
|
1950
|
+
}
|
1951
|
+
if (normal_cmp(mant, min) < 0) {
|
1952
|
+
do {
|
1953
|
+
mant->scale--;
|
1954
|
+
exp--;
|
1955
|
+
} while (normal_cmp(mant, min) < 0);
|
1956
|
+
goto coda;
|
1957
|
+
}
|
1958
|
+
if (normal_cmp(mant, max) >= 0) {
|
1959
|
+
do {
|
1960
|
+
mant->scale++;
|
1961
|
+
exp++;
|
1962
|
+
} while (normal_cmp(mant, max) >= 0);
|
1963
|
+
goto coda;
|
1964
|
+
}
|
1965
|
+
/* x is already normalized, return untouched */
|
1966
|
+
return rb_assoc_new(x, INT2FIX(0));
|
1967
|
+
coda:
|
1968
|
+
/* normalized with changing some values */
|
1969
|
+
if (integer) xfree(d);
|
1970
|
+
if (negative) mant->inum = INUM_UMINUS(mant->inum);
|
1971
|
+
return rb_assoc_new(WrapDecimal(mant), LONG2NUM(exp));
|
1972
|
+
}
|
1973
|
+
|
1974
|
+
/* initialize minimum Math functions in C */
|
1975
|
+
static void
|
1976
|
+
init_math(void)
|
1977
|
+
{
|
1978
|
+
mMath = rb_define_module_under(cDecimal, "Math");
|
1979
|
+
rb_define_module_function(mMath, "ldexp10", math_ldexp10, 2);
|
1980
|
+
rb_define_module_function(mMath, "frexp10", math_frexp10, 1);
|
1981
|
+
}
|
1982
|
+
|
1983
|
+
|
1984
|
+
/*
|
1985
|
+
* +Decimal+ is a decimal fraction that holds exact number in the decimal
|
1986
|
+
* system unlike +Float+. It can hold multi-precision digits, so you can
|
1987
|
+
* calculate any detailed number as you likes.
|
1988
|
+
*
|
1989
|
+
* *FIXME*: write exceptions raised by Float and details about scales
|
1990
|
+
*/
|
1532
1991
|
void
|
1533
1992
|
Init_decimal(void)
|
1534
1993
|
{
|
1535
1994
|
cDecimal = rb_define_class("Decimal", rb_cNumeric);
|
1995
|
+
/* Raised when infinite or NaN was rounded. */
|
1536
1996
|
eDomainError = rb_define_class_under(cDecimal, "DomainError",
|
1537
1997
|
rb_eRangeError);
|
1998
|
+
/*
|
1999
|
+
* Raised when rounding is necessary in spite of a constant
|
2000
|
+
* <code>Decimal::ROUND_UNNECESSARY</code> was passed to
|
2001
|
+
* <code>Decimal#round</code>.
|
2002
|
+
*/
|
1538
2003
|
eArithmeticError = rb_define_class_under(cDecimal, "ArithmeticError",
|
1539
2004
|
rb_eStandardError);
|
1540
2005
|
|
@@ -1543,36 +2008,51 @@ Init_decimal(void)
|
|
1543
2008
|
rb_define_alloc_func(cDecimal, dec_s_allocate);
|
1544
2009
|
rb_define_method(cDecimal, "initialize", dec_initialize, 1);
|
1545
2010
|
|
1546
|
-
/*
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
2011
|
+
/* static objects, should not be freed */
|
2012
|
+
VALUE_PINF = WrapStatic(DEC_PINF);
|
2013
|
+
VALUE_NINF = WrapStatic(DEC_NINF);
|
2014
|
+
VALUE_NaN = WrapStatic(DEC_NaN);
|
2015
|
+
/* and register them with GC */
|
1551
2016
|
rb_global_variable(&VALUE_PINF);
|
1552
2017
|
rb_global_variable(&VALUE_NINF);
|
1553
2018
|
rb_global_variable(&VALUE_NaN);
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
2019
|
+
/* then define as constants */
|
2020
|
+
rb_define_const(cDecimal, "INFINITY", VALUE_PINF);
|
2021
|
+
rb_define_const(cDecimal, "NAN", VALUE_NaN);
|
2022
|
+
|
2023
|
+
/* generated by:
|
2024
|
+
* %w(ceiling down floor half_down half_even half_up up
|
2025
|
+
* unnecessary).each do |s|
|
2026
|
+
* r = "ROUND_#{s.upcase}"
|
2027
|
+
* puts %(#{r} = ID2SYM(rb_intern("#{s}"));)
|
2028
|
+
* puts %(rb_define_const(cDecimal, "#{r}", #{r});)
|
2029
|
+
* end
|
2030
|
+
*/
|
2031
|
+
ROUND_CEILING = ID2SYM(rb_intern("ceiling"));
|
2032
|
+
rb_define_const(cDecimal, "ROUND_CEILING", ROUND_CEILING);
|
2033
|
+
ROUND_DOWN = ID2SYM(rb_intern("down"));
|
2034
|
+
rb_define_const(cDecimal, "ROUND_DOWN", ROUND_DOWN);
|
2035
|
+
ROUND_FLOOR = ID2SYM(rb_intern("floor"));
|
2036
|
+
rb_define_const(cDecimal, "ROUND_FLOOR", ROUND_FLOOR);
|
2037
|
+
ROUND_HALF_DOWN = ID2SYM(rb_intern("half_down"));
|
2038
|
+
rb_define_const(cDecimal, "ROUND_HALF_DOWN", ROUND_HALF_DOWN);
|
2039
|
+
ROUND_HALF_EVEN = ID2SYM(rb_intern("half_even"));
|
2040
|
+
rb_define_const(cDecimal, "ROUND_HALF_EVEN", ROUND_HALF_EVEN);
|
2041
|
+
ROUND_HALF_UP = ID2SYM(rb_intern("half_up"));
|
2042
|
+
rb_define_const(cDecimal, "ROUND_HALF_UP", ROUND_HALF_UP);
|
2043
|
+
ROUND_UP = ID2SYM(rb_intern("up"));
|
2044
|
+
rb_define_const(cDecimal, "ROUND_UP", ROUND_UP);
|
2045
|
+
ROUND_UNNECESSARY = ID2SYM(rb_intern("unnecessary"));
|
2046
|
+
rb_define_const(cDecimal, "ROUND_UNNECESSARY", ROUND_UNNECESSARY);
|
1567
2047
|
|
1568
2048
|
#ifdef DEBUG
|
1569
2049
|
rb_define_method(cDecimal, "scale", dec_scale, 0);
|
1570
2050
|
rb_define_method(cDecimal, "unscaled_value", dec_unscaled_value, 0);
|
1571
|
-
#endif
|
1572
2051
|
rb_define_method(cDecimal, "strip_trailing_zeros",
|
1573
2052
|
dec_strip_trailing_zeros, 0);
|
1574
2053
|
rb_define_method(cDecimal, "strip", dec_strip_trailing_zeros, 0);
|
1575
|
-
|
2054
|
+
#endif
|
2055
|
+
|
1576
2056
|
rb_define_method(cDecimal, "to_s", dec_to_s, 0);
|
1577
2057
|
rb_define_method(cDecimal, "inspect", dec_inspect, 0);
|
1578
2058
|
rb_define_method(cDecimal, "coerce", dec_coerce, 1);
|
@@ -1602,7 +2082,6 @@ Init_decimal(void)
|
|
1602
2082
|
rb_define_method(cDecimal, "zero?", dec_zero_p, 0);
|
1603
2083
|
|
1604
2084
|
rb_define_method(cDecimal, "to_i", dec_to_i, 0);
|
1605
|
-
rb_define_method(cDecimal, "to_int", dec_to_i, 0);
|
1606
2085
|
rb_define_method(cDecimal, "truncate", dec_truncate, -1);
|
1607
2086
|
rb_define_method(cDecimal, "floor", dec_floor, -1);
|
1608
2087
|
rb_define_method(cDecimal, "ceil", dec_ceil, -1);
|
@@ -1611,4 +2090,6 @@ Init_decimal(void)
|
|
1611
2090
|
rb_define_method(cDecimal, "nan?", dec_nan_p, 0);
|
1612
2091
|
rb_define_method(cDecimal, "finite?", dec_finite_p, 0);
|
1613
2092
|
rb_define_method(cDecimal, "infinite?", dec_infinite_p, 0);
|
2093
|
+
|
2094
|
+
init_math();
|
1614
2095
|
}
|