Ruby 4.1.0dev (2026-04-17 revision 11e3c78b61da705c783dd12fb7f158c0d256ede0)
numeric.h
1#ifndef INTERNAL_NUMERIC_H /*-*-C-*-vi:se ft=c:*/
2#define INTERNAL_NUMERIC_H
11#include "internal/bignum.h" /* for BIGNUM_POSITIVE_P */
12#include "internal/bits.h" /* for RUBY_BIT_ROTL */
13#include "internal/compar.h" /* for rb_cmperr_reason */
14#include "internal/fixnum.h" /* for FIXNUM_POSITIVE_P */
15#include "internal/vm.h" /* for rb_method_basic_definition_p */
16#include "ruby/ruby.h" /* for USE_FLONUM */
17
18#define ROUND_TO(mode, even, up, down) \
19 ((mode) == RUBY_NUM_ROUND_HALF_EVEN ? even : \
20 (mode) == RUBY_NUM_ROUND_HALF_UP ? up : down)
21#define ROUND_FUNC(mode, name) \
22 ROUND_TO(mode, name##_half_even, name##_half_up, name##_half_down)
23#define ROUND_CALL(mode, name, args) \
24 ROUND_TO(mode, name##_half_even args, \
25 name##_half_up args, name##_half_down args)
26
27#ifndef ROUND_DEFAULT
28# define ROUND_DEFAULT RUBY_NUM_ROUND_HALF_UP
29#endif
30
31enum ruby_num_rounding_mode {
32 RUBY_NUM_ROUND_HALF_UP,
33 RUBY_NUM_ROUND_HALF_EVEN,
34 RUBY_NUM_ROUND_HALF_DOWN,
35 RUBY_NUM_ROUND_DEFAULT = ROUND_DEFAULT,
36};
37
38/* same as internal.h */
39#define numberof(array) ((int)(sizeof(array) / sizeof((array)[0])))
40#define roomof(x, y) (((x) + (y) - 1) / (y))
41#define type_roomof(x, y) roomof(sizeof(x), sizeof(y))
42
43#if SIZEOF_DOUBLE <= SIZEOF_VALUE
44typedef double rb_float_value_type;
45#else
46typedef struct {
47 VALUE values[roomof(SIZEOF_DOUBLE, SIZEOF_VALUE)];
48} rb_float_value_type;
49#endif
50
51struct RFloat {
52 struct RBasic basic;
53 rb_float_value_type float_value;
54};
55
56#define RFLOAT(obj) ((struct RFloat *)(obj))
57
58/* numeric.c */
59int rb_num_to_uint(VALUE val, unsigned int *ret);
60VALUE ruby_num_interval_step_size(VALUE from, VALUE to, VALUE step, int excl);
61double ruby_float_step_size(double beg, double end, double unit, int excl);
62int ruby_float_step(VALUE from, VALUE to, VALUE step, int excl, int allow_endless);
63int rb_num_negative_p(VALUE);
64VALUE rb_int_succ(VALUE num);
65VALUE rb_float_uminus(VALUE num);
66VALUE rb_int_plus(VALUE x, VALUE y);
67VALUE rb_float_plus(VALUE x, VALUE y);
68VALUE rb_int_minus(VALUE x, VALUE y);
69VALUE rb_float_minus(VALUE x, VALUE y);
70VALUE rb_int_mul(VALUE x, VALUE y);
71VALUE rb_float_mul(VALUE x, VALUE y);
72VALUE rb_float_div(VALUE x, VALUE y);
73VALUE rb_flo_to_i(VALUE num);
74VALUE rb_int_idiv(VALUE x, VALUE y);
75VALUE rb_int_modulo(VALUE x, VALUE y);
76VALUE rb_int2str(VALUE num, int base);
77VALUE rb_fix_plus(VALUE x, VALUE y);
78VALUE rb_int_gt(VALUE x, VALUE y);
79VALUE rb_float_gt(VALUE x, VALUE y);
80VALUE rb_int_ge(VALUE x, VALUE y);
81enum ruby_num_rounding_mode rb_num_get_rounding_option(VALUE opts);
82VALUE rb_int_fdiv(VALUE x, VALUE y);
83double rb_int_fdiv_double(VALUE x, VALUE y);
84VALUE rb_int_pow(VALUE x, VALUE y);
85VALUE rb_float_pow(VALUE x, VALUE y);
86VALUE rb_int_cmp(VALUE x, VALUE y);
87VALUE rb_int_equal(VALUE x, VALUE y);
88VALUE rb_int_divmod(VALUE x, VALUE y);
89VALUE rb_int_and(VALUE x, VALUE y);
90VALUE rb_int_xor(VALUE x, VALUE y);
91VALUE rb_int_lshift(VALUE x, VALUE y);
92VALUE rb_int_rshift(VALUE x, VALUE y);
93VALUE rb_int_div(VALUE x, VALUE y);
94int rb_int_positive_p(VALUE num);
95int rb_int_negative_p(VALUE num);
96VALUE rb_check_integer_type(VALUE);
97VALUE rb_num_pow(VALUE x, VALUE y);
98VALUE rb_float_ceil(VALUE num, int ndigits);
99VALUE rb_float_floor(VALUE x, int ndigits);
100VALUE rb_float_abs(VALUE flt);
101static inline VALUE rb_num_compare_with_zero(VALUE num, ID mid);
102static inline int rb_num_positive_int_p(VALUE num);
103static inline int rb_num_negative_int_p(VALUE num);
104static inline double rb_float_flonum_value(VALUE v);
105static inline double rb_float_noflonum_value(VALUE v);
106static inline double rb_float_value_inline(VALUE v);
107static inline VALUE rb_float_new_inline(double d);
108static inline bool INT_POSITIVE_P(VALUE num);
109static inline bool INT_NEGATIVE_P(VALUE num);
110static inline bool FLOAT_ZERO_P(VALUE num);
111#define rb_float_value rb_float_value_inline
112#define rb_float_new rb_float_new_inline
113
114RUBY_SYMBOL_EXPORT_BEGIN
115/* numeric.c (export) */
116RUBY_SYMBOL_EXPORT_END
117
118VALUE rb_flo_div_flo(VALUE x, VALUE y);
119double ruby_float_mod(double x, double y);
120VALUE rb_float_equal(VALUE x, VALUE y);
121int rb_float_cmp(VALUE x, VALUE y);
122VALUE rb_float_eql(VALUE x, VALUE y);
123VALUE rb_fix_aref(VALUE fix, VALUE idx);
124VALUE rb_int_zero_p(VALUE num);
125VALUE rb_int_even_p(VALUE num);
126VALUE rb_int_odd_p(VALUE num);
127VALUE rb_int_abs(VALUE num);
128VALUE rb_int_bit_length(VALUE num);
129VALUE rb_int_uminus(VALUE num);
130VALUE rb_int_comp(VALUE num);
131
132// Unified 128-bit integer structures that work with or without native support:
134#ifdef WORDS_BIGENDIAN
135 struct {
136 uint64_t high;
137 uint64_t low;
138 } parts;
139#else
140 struct {
141 uint64_t low;
142 uint64_t high;
143 } parts;
144#endif
145#ifdef HAVE_UINT128_T
146 uint128_t value;
147#endif
148};
149typedef union rb_uint128 rb_uint128_t;
150
152#ifdef WORDS_BIGENDIAN
153 struct {
154 uint64_t high;
155 uint64_t low;
156 } parts;
157#else
158 struct {
159 uint64_t low;
160 uint64_t high;
161 } parts;
162#endif
163#ifdef HAVE_UINT128_T
164 int128_t value;
165#endif
166};
167typedef union rb_int128 rb_int128_t;
168
170 rb_uint128_t uint128;
171 rb_int128_t int128;
172};
173
174// Conversion functions for 128-bit integers:
175rb_uint128_t rb_numeric_to_uint128(VALUE x);
176rb_int128_t rb_numeric_to_int128(VALUE x);
177VALUE rb_uint128_to_numeric(rb_uint128_t n);
178VALUE rb_int128_to_numeric(rb_int128_t n);
179
180static inline bool
181INT_POSITIVE_P(VALUE num)
182{
183 if (FIXNUM_P(num)) {
184 return FIXNUM_POSITIVE_P(num);
185 }
186 else {
187 return BIGNUM_POSITIVE_P(num);
188 }
189}
190
191static inline bool
192INT_NEGATIVE_P(VALUE num)
193{
194 if (FIXNUM_P(num)) {
195 return FIXNUM_NEGATIVE_P(num);
196 }
197 else {
198 return BIGNUM_NEGATIVE_P(num);
199 }
200}
201
202static inline bool
203FLOAT_ZERO_P(VALUE num)
204{
205 return RFLOAT_VALUE(num) == 0.0;
206}
207
208static inline VALUE
209rb_num_compare_with_zero(VALUE num, ID mid)
210{
211 VALUE zero = INT2FIX(0);
212 VALUE r = rb_check_funcall(num, mid, 1, &zero);
213 if (RB_UNDEF_P(r)) {
214 rb_cmperr_reason(num, zero, "unable to compare with zero");
215 }
216 return r;
217}
218
219static inline int
220rb_num_positive_int_p(VALUE num)
221{
222 const ID mid = '>';
223
224 if (FIXNUM_P(num)) {
225 if (rb_method_basic_definition_p(rb_cInteger, mid))
226 return FIXNUM_POSITIVE_P(num);
227 }
228 else if (RB_TYPE_P(num, T_BIGNUM)) {
229 if (rb_method_basic_definition_p(rb_cInteger, mid))
230 return BIGNUM_POSITIVE_P(num);
231 }
232 return RTEST(rb_num_compare_with_zero(num, mid));
233}
234
235static inline int
236rb_num_negative_int_p(VALUE num)
237{
238 const ID mid = '<';
239
240 if (FIXNUM_P(num)) {
241 if (rb_method_basic_definition_p(rb_cInteger, mid))
242 return FIXNUM_NEGATIVE_P(num);
243 }
244 else if (RB_TYPE_P(num, T_BIGNUM)) {
245 if (rb_method_basic_definition_p(rb_cInteger, mid))
246 return BIGNUM_NEGATIVE_P(num);
247 }
248 return RTEST(rb_num_compare_with_zero(num, mid));
249}
250
251static inline double
252rb_float_flonum_value(VALUE v)
253{
254#if USE_FLONUM
255 if (v != (VALUE)0x8000000000000002) { /* LIKELY */
256 union {
257 double d;
258 VALUE v;
259 } t;
260
261 VALUE b63 = (v >> 63);
262 /* e: xx1... -> 011... */
263 /* xx0... -> 100... */
264 /* ^b63 */
265 t.v = RUBY_BIT_ROTR((2 - b63) | (v & ~(VALUE)0x03), 3);
266 return t.d;
267 }
268#endif
269 return 0.0;
270}
271
272static inline double
273rb_float_noflonum_value(VALUE v)
274{
275#if SIZEOF_DOUBLE <= SIZEOF_VALUE
276 return RFLOAT(v)->float_value;
277#else
278 union {
279 rb_float_value_type v;
280 double d;
281 } u = {RFLOAT(v)->float_value};
282 return u.d;
283#endif
284}
285
286static inline double
287rb_float_value_inline(VALUE v)
288{
289 if (FLONUM_P(v)) {
290 return rb_float_flonum_value(v);
291 }
292 return rb_float_noflonum_value(v);
293}
294
295static inline VALUE
296rb_float_new_inline(double d)
297{
298#if USE_FLONUM
299 union {
300 double d;
301 VALUE v;
302 } t;
303 int bits;
304
305 t.d = d;
306 bits = (int)((VALUE)(t.v >> 60) & 0x7);
307 /* bits contains 3 bits of b62..b60. */
308 /* bits - 3 = */
309 /* b011 -> b000 */
310 /* b100 -> b001 */
311
312 if (t.v != 0x3000000000000000 /* 1.72723e-77 */ &&
313 !((bits-3) & ~0x01)) {
314 return (RUBY_BIT_ROTL(t.v, 3) & ~(VALUE)0x01) | 0x02;
315 }
316 else if (t.v == (VALUE)0) {
317 /* +0.0 */
318 return 0x8000000000000002;
319 }
320 /* out of range */
321#endif
322 return rb_float_new_in_heap(d);
323}
324
325#endif /* INTERNAL_NUMERIC_H */
VALUE rb_float_new_in_heap(double d)
Identical to rb_float_new(), except it does not generate Flonums.
Definition numeric.c:911
#define RFLOAT_VALUE
Old name of rb_float_value.
Definition double.h:28
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define T_BIGNUM
Old name of RUBY_T_BIGNUM.
Definition value_type.h:57
#define FLONUM_P
Old name of RB_FLONUM_P.
#define FIXNUM_P
Old name of RB_FIXNUM_P.
VALUE rb_cInteger
Module class.
Definition numeric.c:199
VALUE rb_check_funcall(VALUE recv, ID mid, int argc, const VALUE *argv)
Identical to rb_funcallv(), except it returns RUBY_Qundef instead of raising rb_eNoMethodError.
Definition vm_eval.c:689
static bool RB_UNDEF_P(VALUE obj)
Checks if the given object is undef.
#define RTEST
This is an old name of RB_TEST.
Ruby object's base components.
Definition rbasic.h:69
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
#define SIZEOF_VALUE
Identical to sizeof(VALUE), except it is a macro that can also be used inside of preprocessor directi...
Definition value.h:69
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376