summaryrefslogtreecommitdiff
path: root/internal/numeric.h
blob: 89bc54b307ff6aa26f8926ee4e885c11925909f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
#ifndef INTERNAL_NUMERIC_H                               /*-*-C-*-vi:se ft=c:*/
#define INTERNAL_NUMERIC_H
/**
 * @author     Ruby developers <ruby-core@ruby-lang.org>
 * @copyright  This  file  is   a  part  of  the   programming  language  Ruby.
 *             Permission  is hereby  granted,  to  either redistribute  and/or
 *             modify this file, provided that  the conditions mentioned in the
 *             file COPYING are met.  Consult the file for details.
 * @brief      Internal header for Numeric.
 */
#include "internal/bignum.h"    /* for BIGNUM_POSITIVE_P */
#include "internal/bits.h"      /* for RUBY_BIT_ROTL */
#include "internal/fixnum.h"    /* for FIXNUM_POSITIVE_P */
#include "internal/vm.h"        /* for rb_method_basic_definition_p */
#include "ruby/intern.h"        /* for rb_cmperr */
#include "ruby/ruby.h"          /* for USE_FLONUM */

#define ROUND_TO(mode, even, up, down) \
    ((mode) == RUBY_NUM_ROUND_HALF_EVEN ? even : \
     (mode) == RUBY_NUM_ROUND_HALF_UP ? up : down)
#define ROUND_FUNC(mode, name) \
    ROUND_TO(mode, name##_half_even, name##_half_up, name##_half_down)
#define ROUND_CALL(mode, name, args) \
    ROUND_TO(mode, name##_half_even args, \
             name##_half_up args, name##_half_down args)

#ifndef ROUND_DEFAULT
# define ROUND_DEFAULT RUBY_NUM_ROUND_HALF_UP
#endif

enum ruby_num_rounding_mode {
    RUBY_NUM_ROUND_HALF_UP,
    RUBY_NUM_ROUND_HALF_EVEN,
    RUBY_NUM_ROUND_HALF_DOWN,
    RUBY_NUM_ROUND_DEFAULT = ROUND_DEFAULT,
};

/* same as internal.h */
#define numberof(array) ((int)(sizeof(array) / sizeof((array)[0])))
#define roomof(x, y) (((x) + (y) - 1) / (y))
#define type_roomof(x, y) roomof(sizeof(x), sizeof(y))

#if SIZEOF_DOUBLE <= SIZEOF_VALUE
typedef double rb_float_value_type;
#else
typedef struct {
    VALUE values[roomof(SIZEOF_DOUBLE, SIZEOF_VALUE)];
} rb_float_value_type;
#endif

struct RFloat {
    struct RBasic basic;
    rb_float_value_type float_value;
};

#define RFLOAT(obj)  ((struct RFloat *)(obj))

/* numeric.c */
int rb_num_to_uint(VALUE val, unsigned int *ret);
VALUE ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl);
double ruby_float_step_size(double beg, double end, double unit, int excl);
int ruby_float_step(VALUE from, VALUE to, VALUE step, int excl, int allow_endless);
int rb_num_negative_p(VALUE);
VALUE rb_int_succ(VALUE num);
VALUE rb_float_uminus(VALUE num);
VALUE rb_int_plus(VALUE x, VALUE y);
VALUE rb_float_plus(VALUE x, VALUE y);
VALUE rb_int_minus(VALUE x, VALUE y);
VALUE rb_float_minus(VALUE x, VALUE y);
VALUE rb_int_mul(VALUE x, VALUE y);
VALUE rb_float_mul(VALUE x, VALUE y);
VALUE rb_float_div(VALUE x, VALUE y);
VALUE rb_int_idiv(VALUE x, VALUE y);
VALUE rb_int_modulo(VALUE x, VALUE y);
VALUE rb_int2str(VALUE num, int base);
VALUE rb_fix_plus(VALUE x, VALUE y);
VALUE rb_int_gt(VALUE x, VALUE y);
VALUE rb_float_gt(VALUE x, VALUE y);
VALUE rb_int_ge(VALUE x, VALUE y);
enum ruby_num_rounding_mode rb_num_get_rounding_option(VALUE opts);
double rb_int_fdiv_double(VALUE x, VALUE y);
VALUE rb_int_pow(VALUE x, VALUE y);
VALUE rb_float_pow(VALUE x, VALUE y);
VALUE rb_int_cmp(VALUE x, VALUE y);
VALUE rb_int_equal(VALUE x, VALUE y);
VALUE rb_int_divmod(VALUE x, VALUE y);
VALUE rb_int_and(VALUE x, VALUE y);
VALUE rb_int_lshift(VALUE x, VALUE y);
VALUE rb_int_div(VALUE x, VALUE y);
int rb_int_positive_p(VALUE num);
int rb_int_negative_p(VALUE num);
VALUE rb_check_integer_type(VALUE);
VALUE rb_num_pow(VALUE x, VALUE y);
VALUE rb_float_ceil(VALUE num, int ndigits);
VALUE rb_float_floor(VALUE x, int ndigits);
VALUE rb_float_abs(VALUE flt);
static inline VALUE rb_num_compare_with_zero(VALUE num, ID mid);
static inline int rb_num_positive_int_p(VALUE num);
static inline int rb_num_negative_int_p(VALUE num);
static inline double rb_float_flonum_value(VALUE v);
static inline double rb_float_noflonum_value(VALUE v);
static inline double rb_float_value_inline(VALUE v);
static inline VALUE rb_float_new_inline(double d);
static inline bool INT_POSITIVE_P(VALUE num);
static inline bool INT_NEGATIVE_P(VALUE num);
static inline bool FLOAT_ZERO_P(VALUE num);
#define rb_float_value rb_float_value_inline
#define rb_float_new   rb_float_new_inline

RUBY_SYMBOL_EXPORT_BEGIN
/* numeric.c (export) */
RUBY_SYMBOL_EXPORT_END

MJIT_SYMBOL_EXPORT_BEGIN
VALUE rb_flo_div_flo(VALUE x, VALUE y);
double ruby_float_mod(double x, double y);
VALUE rb_float_equal(VALUE x, VALUE y);
int rb_float_cmp(VALUE x, VALUE y);
VALUE rb_float_eql(VALUE x, VALUE y);
VALUE rb_fix_aref(VALUE fix, VALUE idx);
VALUE rb_int_zero_p(VALUE num);
VALUE rb_int_even_p(VALUE num);
VALUE rb_int_odd_p(VALUE num);
VALUE rb_int_abs(VALUE num);
VALUE rb_int_bit_length(VALUE num);
VALUE rb_int_uminus(VALUE num);
VALUE rb_int_comp(VALUE num);
MJIT_SYMBOL_EXPORT_END

static inline bool
INT_POSITIVE_P(VALUE num)
{
    if (FIXNUM_P(num)) {
        return FIXNUM_POSITIVE_P(num);
    }
    else {
        return BIGNUM_POSITIVE_P(num);
    }
}

static inline bool
INT_NEGATIVE_P(VALUE num)
{
    if (FIXNUM_P(num)) {
        return FIXNUM_NEGATIVE_P(num);
    }
    else {
        return BIGNUM_NEGATIVE_P(num);
    }
}

static inline bool
FLOAT_ZERO_P(VALUE num)
{
    return RFLOAT_VALUE(num) == 0.0;
}

static inline VALUE
rb_num_compare_with_zero(VALUE num, ID mid)
{
    VALUE zero = INT2FIX(0);
    VALUE r = rb_check_funcall(num, mid, 1, &zero);
    if (r == Qundef) {
        rb_cmperr(num, zero);
    }
    return r;
}

static inline int
rb_num_positive_int_p(VALUE num)
{
    const ID mid = '>';

    if (FIXNUM_P(num)) {
        if (rb_method_basic_definition_p(rb_cInteger, mid))
            return FIXNUM_POSITIVE_P(num);
    }
    else if (RB_TYPE_P(num, T_BIGNUM)) {
        if (rb_method_basic_definition_p(rb_cInteger, mid))
            return BIGNUM_POSITIVE_P(num);
    }
    return RTEST(rb_num_compare_with_zero(num, mid));
}

static inline int
rb_num_negative_int_p(VALUE num)
{
    const ID mid = '<';

    if (FIXNUM_P(num)) {
        if (rb_method_basic_definition_p(rb_cInteger, mid))
            return FIXNUM_NEGATIVE_P(num);
    }
    else if (RB_TYPE_P(num, T_BIGNUM)) {
        if (rb_method_basic_definition_p(rb_cInteger, mid))
            return BIGNUM_NEGATIVE_P(num);
    }
    return RTEST(rb_num_compare_with_zero(num, mid));
}

static inline double
rb_float_flonum_value(VALUE v)
{
#if USE_FLONUM
    if (v != (VALUE)0x8000000000000002) { /* LIKELY */
        union {
            double d;
            VALUE v;
        } t;

        VALUE b63 = (v >> 63);
        /* e: xx1... -> 011... */
        /*    xx0... -> 100... */
        /*      ^b63           */
        t.v = RUBY_BIT_ROTR((2 - b63) | (v & ~(VALUE)0x03), 3);
        return t.d;
    }
#endif
    return 0.0;
}

static inline double
rb_float_noflonum_value(VALUE v)
{
#if SIZEOF_DOUBLE <= SIZEOF_VALUE
    return RFLOAT(v)->float_value;
#else
    union {
        rb_float_value_type v;
        double d;
    } u = {RFLOAT(v)->float_value};
    return u.d;
#endif
}

static inline double
rb_float_value_inline(VALUE v)
{
    if (FLONUM_P(v)) {
        return rb_float_flonum_value(v);
    }
    return rb_float_noflonum_value(v);
}

static inline VALUE
rb_float_new_inline(double d)
{
#if USE_FLONUM
    union {
        double d;
        VALUE v;
    } t;
    int bits;

    t.d = d;
    bits = (int)((VALUE)(t.v >> 60) & 0x7);
    /* bits contains 3 bits of b62..b60. */
    /* bits - 3 = */
    /*   b011 -> b000 */
    /*   b100 -> b001 */

    if (t.v != 0x3000000000000000 /* 1.72723e-77 */ &&
        !((bits-3) & ~0x01)) {
        return (RUBY_BIT_ROTL(t.v, 3) & ~(VALUE)0x01) | 0x02;
    }
    else if (t.v == (VALUE)0) {
        /* +0.0 */
        return 0x8000000000000002;
    }
    /* out of range */
#endif
    return rb_float_new_in_heap(d);
}

#endif /* INTERNAL_NUMERIC_H */