summaryrefslogtreecommitdiff
path: root/numeric.c
diff options
context:
space:
mode:
Diffstat (limited to 'numeric.c')
-rw-r--r--numeric.c641
1 files changed, 407 insertions, 234 deletions
diff --git a/numeric.c b/numeric.c
index 20c1b50866..e8df2a6aa0 100644
--- a/numeric.c
+++ b/numeric.c
@@ -576,7 +576,7 @@ num_imaginary(VALUE num)
* call-seq:
* -self -> numeric
*
- * Unary Minus---Returns the receiver, negated.
+ * Returns +self+, negated.
*/
static VALUE
@@ -595,8 +595,8 @@ num_uminus(VALUE num)
* fdiv(other) -> float
*
* Returns the quotient <tt>self/other</tt> as a float,
- * using method +/+ in the derived class of +self+.
- * (\Numeric itself does not define method +/+.)
+ * using method +/+ as defined in the subclass of \Numeric.
+ * (\Numeric itself does not define +/+.)
*
* Of the Core and Standard Library classes,
* only BigDecimal uses this implementation.
@@ -614,8 +614,8 @@ num_fdiv(VALUE x, VALUE y)
* div(other) -> integer
*
* Returns the quotient <tt>self/other</tt> as an integer (via +floor+),
- * using method +/+ in the derived class of +self+.
- * (\Numeric itself does not define method +/+.)
+ * using method +/+ as defined in the subclass of \Numeric.
+ * (\Numeric itself does not define +/+.)
*
* Of the Core and Standard Library classes,
* Only Float and Rational use this implementation.
@@ -633,7 +633,7 @@ num_div(VALUE x, VALUE y)
* call-seq:
* self % other -> real_numeric
*
- * Returns +self+ modulo +other+ as a real number.
+ * Returns +self+ modulo +other+ as a real numeric (\Integer, \Float, or \Rational).
*
* Of the Core and Standard Library classes,
* only Rational uses this implementation.
@@ -829,7 +829,7 @@ rb_int_zero_p(VALUE num)
* Of the Core and Standard Library classes,
* Integer, Float, Rational, and Complex use this implementation.
*
- * Related: #zero?
+ * Related: #zero?
*
*/
@@ -847,7 +847,8 @@ num_nonzero_p(VALUE num)
* to_int -> integer
*
* Returns +self+ as an integer;
- * converts using method +to_i+ in the derived class.
+ * converts using method +to_i+ in the subclass of \Numeric.
+ * (\Numeric itself does not define +to_i+.)
*
* Of the Core and Standard Library classes,
* only Rational and Complex use this implementation.
@@ -857,7 +858,7 @@ num_nonzero_p(VALUE num)
* Rational(1, 2).to_int # => 0
* Rational(2, 1).to_int # => 2
* Complex(2, 0).to_int # => 2
- * Complex(2, 1) # Raises RangeError (non-zero imaginary part)
+ * Complex(2, 1).to_int # Raises RangeError (non-zero imaginary part)
*
*/
@@ -905,88 +906,6 @@ num_negative_p(VALUE num)
return RBOOL(rb_num_negative_int_p(num));
}
-
-/********************************************************************
- *
- * Document-class: Float
- *
- * A \Float object represents a sometimes-inexact real number using the native
- * architecture's double-precision floating point representation.
- *
- * Floating point has a different arithmetic and is an inexact number.
- * So you should know its esoteric system. See following:
- *
- * - https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
- * - https://github.com/rdp/ruby_tutorials_core/wiki/Ruby-Talk-FAQ#-why-are-rubys-floats-imprecise
- * - https://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
- *
- * You can create a \Float object explicitly with:
- *
- * - A {floating-point literal}[rdoc-ref:syntax/literals.rdoc@Float+Literals].
- *
- * You can convert certain objects to Floats with:
- *
- * - \Method #Float.
- *
- * == What's Here
- *
- * First, what's elsewhere. \Class \Float:
- *
- * - Inherits from
- * {class Numeric}[rdoc-ref:Numeric@What-27s+Here]
- * and {class Object}[rdoc-ref:Object@What-27s+Here].
- * - Includes {module Comparable}[rdoc-ref:Comparable@What-27s+Here].
- *
- * Here, class \Float provides methods for:
- *
- * - {Querying}[rdoc-ref:Float@Querying]
- * - {Comparing}[rdoc-ref:Float@Comparing]
- * - {Converting}[rdoc-ref:Float@Converting]
- *
- * === Querying
- *
- * - #finite?: Returns whether +self+ is finite.
- * - #hash: Returns the integer hash code for +self+.
- * - #infinite?: Returns whether +self+ is infinite.
- * - #nan?: Returns whether +self+ is a NaN (not-a-number).
- *
- * === Comparing
- *
- * - #<: Returns whether +self+ is less than the given value.
- * - #<=: Returns whether +self+ is less than or equal to the given value.
- * - #<=>: Returns a number indicating whether +self+ is less than, equal
- * to, or greater than the given value.
- * - #== (aliased as #=== and #eql?): Returns whether +self+ is equal to
- * the given value.
- * - #>: Returns whether +self+ is greater than the given value.
- * - #>=: Returns whether +self+ is greater than or equal to the given value.
- *
- * === Converting
- *
- * - #% (aliased as #modulo): Returns +self+ modulo the given value.
- * - #*: Returns the product of +self+ and the given value.
- * - #**: Returns the value of +self+ raised to the power of the given value.
- * - #+: Returns the sum of +self+ and the given value.
- * - #-: Returns the difference of +self+ and the given value.
- * - #/: Returns the quotient of +self+ and the given value.
- * - #ceil: Returns the smallest number greater than or equal to +self+.
- * - #coerce: Returns a 2-element array containing the given value converted to a \Float
- * and +self+
- * - #divmod: Returns a 2-element array containing the quotient and remainder
- * results of dividing +self+ by the given value.
- * - #fdiv: Returns the \Float result of dividing +self+ by the given value.
- * - #floor: Returns the greatest number smaller than or equal to +self+.
- * - #next_float: Returns the next-larger representable \Float.
- * - #prev_float: Returns the next-smaller representable \Float.
- * - #quo: Returns the quotient from dividing +self+ by the given value.
- * - #round: Returns +self+ rounded to the nearest value, to a given precision.
- * - #to_i (aliased as #to_int): Returns +self+ truncated to an Integer.
- * - #to_s (aliased as #inspect): Returns a string containing the place-value
- * representation of +self+ in the given radix.
- * - #truncate: Returns +self+ truncated to a given precision.
- *
- */
-
VALUE
rb_float_new_in_heap(double d)
{
@@ -1131,15 +1050,21 @@ rb_float_uminus(VALUE flt)
/*
* call-seq:
- * self + other -> numeric
+ * self + other -> float or complex
*
- * Returns a new \Float which is the sum of +self+ and +other+:
+ * Returns the sum of +self+ and +other+;
+ * the result may be inexact (see Float):
*
- * f = 3.14
- * f + 1 # => 4.140000000000001
- * f + 1.0 # => 4.140000000000001
- * f + Rational(1, 1) # => 4.140000000000001
- * f + Complex(1, 0) # => (4.140000000000001+0i)
+ * 3.14 + 0 # => 3.14
+ * 3.14 + 1 # => 4.140000000000001
+ * -3.14 + 0 # => -3.14
+ * -3.14 + 1 # => -2.14
+
+ * 3.14 + -3.14 # => 0.0
+ * -3.14 + -3.14 # => -6.28
+ *
+ * 3.14 + Complex(1, 0) # => (4.140000000000001+0i)
+ * 3.14 + Rational(1, 1) # => 4.140000000000001
*
*/
@@ -1164,7 +1089,7 @@ rb_float_plus(VALUE x, VALUE y)
* call-seq:
* self - other -> numeric
*
- * Returns a new \Float which is the difference of +self+ and +other+:
+ * Returns the difference of +self+ and +other+:
*
* f = 3.14
* f - 1 # => 2.14
@@ -1195,13 +1120,14 @@ rb_float_minus(VALUE x, VALUE y)
* call-seq:
* self * other -> numeric
*
- * Returns a new \Float which is the product of +self+ and +other+:
+ * Returns the numeric product of +self+ and +other+:
*
* f = 3.14
* f * 2 # => 6.28
* f * 2.0 # => 6.28
* f * Rational(1, 2) # => 1.57
* f * Complex(2, 0) # => (6.28+0.0i)
+ *
*/
VALUE
@@ -1249,7 +1175,7 @@ rb_flo_div_flo(VALUE x, VALUE y)
* call-seq:
* self / other -> numeric
*
- * Returns a new \Float which is the result of dividing +self+ by +other+:
+ * Returns the quotient of +self+ and +other+:
*
* f = 3.14
* f / 2 # => 1.57
@@ -1358,7 +1284,7 @@ ruby_float_mod(double x, double y)
* call-seq:
* self % other -> float
*
- * Returns +self+ modulo +other+ as a float.
+ * Returns +self+ modulo +other+ as a \Float.
*
* For float +f+ and real number +r+, these expressions are equivalent:
*
@@ -1464,9 +1390,9 @@ flo_divmod(VALUE x, VALUE y)
/*
* call-seq:
- * self ** other -> numeric
+ * self ** exponent -> numeric
*
- * Raises +self+ to the power of +other+:
+ * Returns +self+ raised to the power +exponent+:
*
* f = 3.14
* f ** 2 # => 9.8596
@@ -1521,7 +1447,7 @@ rb_float_pow(VALUE x, VALUE y)
* 1.eql?(Rational(1, 1)) # => false
* 1.eql?(Complex(1, 0)) # => false
*
- * \Method +eql?+ is different from <tt>==</tt> in that +eql?+ requires matching types,
+ * Method +eql?+ is different from <tt>==</tt> in that +eql?+ requires matching types,
* while <tt>==</tt> does not.
*
*/
@@ -1542,10 +1468,17 @@ num_eql(VALUE x, VALUE y)
* call-seq:
* self <=> other -> zero or nil
*
- * Returns zero if +self+ is the same as +other+, +nil+ otherwise.
+ * Compares +self+ and +other+.
*
- * No subclass in the Ruby Core or Standard Library uses this implementation.
+ * Returns:
+ *
+ * - Zero, if +self+ is the same as +other+.
+ * - +nil+, otherwise.
*
+ * \Class \Numeric includes module Comparable,
+ * each of whose methods uses Numeric#<=> for comparison.
+ *
+ * No subclass in the Ruby Core or Standard Library uses this implementation.
*/
static VALUE
@@ -1568,7 +1501,7 @@ num_equal(VALUE x, VALUE y)
* call-seq:
* self == other -> true or false
*
- * Returns +true+ if +other+ has the same value as +self+, +false+ otherwise:
+ * Returns whether +other+ is numerically equal to +self+:
*
* 2.0 == 2 # => true
* 2.0 == 2.0 # => true
@@ -1591,17 +1524,11 @@ rb_float_equal(VALUE x, VALUE y)
}
else if (RB_FLOAT_TYPE_P(y)) {
b = RFLOAT_VALUE(y);
-#if MSC_VERSION_BEFORE(1300)
- if (isnan(b)) return Qfalse;
-#endif
}
else {
return num_equal(x, y);
}
a = RFLOAT_VALUE(x);
-#if MSC_VERSION_BEFORE(1300)
- if (isnan(a)) return Qfalse;
-#endif
return RBOOL(a == b);
}
@@ -1641,30 +1568,32 @@ rb_dbl_cmp(double a, double b)
/*
* call-seq:
- * self <=> other -> -1, 0, +1, or nil
+ * self <=> other -> -1, 0, 1, or nil
+ *
+ * Compares +self+ and +other+.
*
- * Returns a value that depends on the numeric relation
- * between +self+ and +other+:
+ * Returns:
*
- * - -1, if +self+ is less than +other+.
- * - 0, if +self+ is equal to +other+.
- * - 1, if +self+ is greater than +other+.
+ * - +-1+, if +self+ is less than +other+.
+ * - +0+, if +self+ is equal to +other+.
+ * - +1+, if +self+ is greater than +other+.
* - +nil+, if the two values are incommensurate.
*
* Examples:
*
+ * 2.0 <=> 2.1 # => -1
* 2.0 <=> 2 # => 0
* 2.0 <=> 2.0 # => 0
* 2.0 <=> Rational(2, 1) # => 0
* 2.0 <=> Complex(2, 0) # => 0
* 2.0 <=> 1.9 # => 1
- * 2.0 <=> 2.1 # => -1
* 2.0 <=> 'foo' # => nil
*
- * This is the basis for the tests in the Comparable module.
- *
* <tt>Float::NAN <=> Float::NAN</tt> returns an implementation-dependent value.
*
+ * \Class \Float includes module Comparable,
+ * each of whose methods uses Float#<=> for comparison.
+ *
*/
static VALUE
@@ -1709,7 +1638,8 @@ rb_float_cmp(VALUE x, VALUE y)
* call-seq:
* self > other -> true or false
*
- * Returns +true+ if +self+ is numerically greater than +other+:
+ * Returns whether the value of +self+ is greater than the value of +other+;
+ * +other+ must be numeric, but may not be Complex:
*
* 2.0 > 1 # => true
* 2.0 > 1.0 # => true
@@ -1734,16 +1664,10 @@ rb_float_gt(VALUE x, VALUE y)
}
else if (RB_FLOAT_TYPE_P(y)) {
b = RFLOAT_VALUE(y);
-#if MSC_VERSION_BEFORE(1300)
- if (isnan(b)) return Qfalse;
-#endif
}
else {
return rb_num_coerce_relop(x, y, '>');
}
-#if MSC_VERSION_BEFORE(1300)
- if (isnan(a)) return Qfalse;
-#endif
return RBOOL(a > b);
}
@@ -1751,7 +1675,8 @@ rb_float_gt(VALUE x, VALUE y)
* call-seq:
* self >= other -> true or false
*
- * Returns +true+ if +self+ is numerically greater than or equal to +other+:
+ * Returns whether the value of +self+ is greater than or equal to the value of +other+;
+ * +other+ must be numeric, but may not be Complex:
*
* 2.0 >= 1 # => true
* 2.0 >= 1.0 # => true
@@ -1777,16 +1702,10 @@ flo_ge(VALUE x, VALUE y)
}
else if (RB_FLOAT_TYPE_P(y)) {
b = RFLOAT_VALUE(y);
-#if MSC_VERSION_BEFORE(1300)
- if (isnan(b)) return Qfalse;
-#endif
}
else {
return rb_num_coerce_relop(x, y, idGE);
}
-#if MSC_VERSION_BEFORE(1300)
- if (isnan(a)) return Qfalse;
-#endif
return RBOOL(a >= b);
}
@@ -1794,7 +1713,8 @@ flo_ge(VALUE x, VALUE y)
* call-seq:
* self < other -> true or false
*
- * Returns +true+ if +self+ is numerically less than +other+:
+ * Returns whether the value of +self+ is less than the value of +other+;
+ * +other+ must be numeric, but may not be Complex:
*
* 2.0 < 3 # => true
* 2.0 < 3.0 # => true
@@ -1802,7 +1722,6 @@ flo_ge(VALUE x, VALUE y)
* 2.0 < 2.0 # => false
*
* <tt>Float::NAN < Float::NAN</tt> returns an implementation-dependent value.
- *
*/
static VALUE
@@ -1819,16 +1738,10 @@ flo_lt(VALUE x, VALUE y)
}
else if (RB_FLOAT_TYPE_P(y)) {
b = RFLOAT_VALUE(y);
-#if MSC_VERSION_BEFORE(1300)
- if (isnan(b)) return Qfalse;
-#endif
}
else {
return rb_num_coerce_relop(x, y, '<');
}
-#if MSC_VERSION_BEFORE(1300)
- if (isnan(a)) return Qfalse;
-#endif
return RBOOL(a < b);
}
@@ -1836,7 +1749,8 @@ flo_lt(VALUE x, VALUE y)
* call-seq:
* self <= other -> true or false
*
- * Returns +true+ if +self+ is numerically less than or equal to +other+:
+ * Returns whether the value of +self+ is less than or equal to the value of +other+;
+ * +other+ must be numeric, but may not be Complex:
*
* 2.0 <= 3 # => true
* 2.0 <= 3.0 # => true
@@ -1862,16 +1776,10 @@ flo_le(VALUE x, VALUE y)
}
else if (RB_FLOAT_TYPE_P(y)) {
b = RFLOAT_VALUE(y);
-#if MSC_VERSION_BEFORE(1300)
- if (isnan(b)) return Qfalse;
-#endif
}
else {
return rb_num_coerce_relop(x, y, idLE);
}
-#if MSC_VERSION_BEFORE(1300)
- if (isnan(a)) return Qfalse;
-#endif
return RBOOL(a <= b);
}
@@ -1899,10 +1807,7 @@ rb_float_eql(VALUE x, VALUE y)
if (RB_FLOAT_TYPE_P(y)) {
double a = RFLOAT_VALUE(x);
double b = RFLOAT_VALUE(y);
-#if MSC_VERSION_BEFORE(1300)
- if (isnan(a) || isnan(b)) return Qfalse;
-#endif
- return RBOOL(a == b);
+ return RBOOL(a == b);
}
return Qfalse;
}
@@ -2708,7 +2613,7 @@ flo_truncate(int argc, VALUE *argv, VALUE num)
* floor(ndigits = 0) -> float or integer
*
* Returns the largest float or integer that is less than or equal to +self+,
- * as specified by the given `ndigits`,
+ * as specified by the given +ndigits+,
* which must be an
* {integer-convertible object}[rdoc-ref:implicit_conversion.rdoc@Integer-Convertible+Objects].
*
@@ -2728,7 +2633,7 @@ num_floor(int argc, VALUE *argv, VALUE num)
* ceil(ndigits = 0) -> float or integer
*
* Returns the smallest float or integer that is greater than or equal to +self+,
- * as specified by the given `ndigits`,
+ * as specified by the given +ndigits+,
* which must be an
* {integer-convertible object}[rdoc-ref:implicit_conversion.rdoc@Integer-Convertible+Objects].
*
@@ -3527,6 +3432,232 @@ rb_num2ull(VALUE val)
#endif /* HAVE_LONG_LONG */
+// Conversion functions for unified 128-bit integer structures,
+// These work with or without native 128-bit integer support.
+
+#ifndef HAVE_UINT128_T
+// Helper function to build 128-bit value from bignum digits (fallback path).
+static inline void
+rb_uint128_from_bignum_digits_fallback(rb_uint128_t *result, BDIGIT *digits, size_t length)
+{
+ // Build the 128-bit value from bignum digits:
+ for (long i = length - 1; i >= 0; i--) {
+ // Shift both low and high parts:
+ uint64_t carry = result->parts.low >> (64 - (SIZEOF_BDIGIT * CHAR_BIT));
+ result->parts.low = (result->parts.low << (SIZEOF_BDIGIT * CHAR_BIT)) | digits[i];
+ result->parts.high = (result->parts.high << (SIZEOF_BDIGIT * CHAR_BIT)) | carry;
+ }
+}
+
+// Helper function to convert absolute value of negative bignum to two's complement.
+// Ruby stores negative bignums as absolute values, so we need to convert to two's complement.
+static inline void
+rb_uint128_twos_complement_negate(rb_uint128_t *value)
+{
+ if (value->parts.low == 0) {
+ value->parts.high = ~value->parts.high + 1;
+ }
+ else {
+ value->parts.low = ~value->parts.low + 1;
+ value->parts.high = ~value->parts.high + (value->parts.low == 0 ? 1 : 0);
+ }
+}
+#endif
+
+rb_uint128_t
+rb_numeric_to_uint128(VALUE x)
+{
+ rb_uint128_t result = {0};
+ if (RB_FIXNUM_P(x)) {
+ long value = RB_FIX2LONG(x);
+ if (value < 0) {
+ rb_raise(rb_eRangeError, "negative integer cannot be converted to unsigned 128-bit integer");
+ }
+#ifdef HAVE_UINT128_T
+ result.value = (uint128_t)value;
+#else
+ result.parts.low = (uint64_t)value;
+ result.parts.high = 0;
+#endif
+ return result;
+ }
+ else if (RB_BIGNUM_TYPE_P(x)) {
+ if (BIGNUM_NEGATIVE_P(x)) {
+ rb_raise(rb_eRangeError, "negative integer cannot be converted to unsigned 128-bit integer");
+ }
+ size_t length = BIGNUM_LEN(x);
+#ifdef HAVE_UINT128_T
+ if (length > roomof(SIZEOF_INT128_T, SIZEOF_BDIGIT)) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into 'unsigned 128-bit integer'");
+ }
+ BDIGIT *digits = BIGNUM_DIGITS(x);
+ result.value = 0;
+ for (long i = length - 1; i >= 0; i--) {
+ result.value = (result.value << (SIZEOF_BDIGIT * CHAR_BIT)) | digits[i];
+ }
+#else
+ // Check if bignum fits in 128 bits (16 bytes)
+ if (length > roomof(16, SIZEOF_BDIGIT)) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into 'unsigned 128-bit integer'");
+ }
+ BDIGIT *digits = BIGNUM_DIGITS(x);
+ rb_uint128_from_bignum_digits_fallback(&result, digits, length);
+#endif
+ return result;
+ }
+ else {
+ rb_raise(rb_eTypeError, "not an integer");
+ }
+}
+
+rb_int128_t
+rb_numeric_to_int128(VALUE x)
+{
+ rb_int128_t result = {0};
+ if (RB_FIXNUM_P(x)) {
+ long value = RB_FIX2LONG(x);
+#ifdef HAVE_UINT128_T
+ result.value = (int128_t)value;
+#else
+ if (value < 0) {
+ // Two's complement representation: for negative values, sign extend
+ // Convert to unsigned: for -1, we want all bits set
+ result.parts.low = (uint64_t)value; // This will be the two's complement representation
+ result.parts.high = UINT64_MAX; // Sign extend: all bits set for negative
+ }
+ else {
+ result.parts.low = (uint64_t)value;
+ result.parts.high = 0;
+ }
+#endif
+ return result;
+ }
+ else if (RB_BIGNUM_TYPE_P(x)) {
+ size_t length = BIGNUM_LEN(x);
+#ifdef HAVE_UINT128_T
+ if (length > roomof(SIZEOF_INT128_T, SIZEOF_BDIGIT)) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into 'signed 128-bit integer'");
+ }
+ BDIGIT *digits = BIGNUM_DIGITS(x);
+ uint128_t unsigned_result = 0;
+ for (long i = length - 1; i >= 0; i--) {
+ unsigned_result = (unsigned_result << (SIZEOF_BDIGIT * CHAR_BIT)) | digits[i];
+ }
+ if (BIGNUM_NEGATIVE_P(x)) {
+ // Convert from two's complement
+ // Maximum negative value is 2^127
+ if (unsigned_result > ((uint128_t)1 << 127)) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into 'signed 128-bit integer'");
+ }
+ result.value = -(int128_t)(unsigned_result - 1) - 1;
+ }
+ else {
+ // Maximum positive value is 2^127 - 1
+ if (unsigned_result > (((uint128_t)1 << 127) - 1)) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into 'signed 128-bit integer'");
+ }
+ result.value = (int128_t)unsigned_result;
+ }
+#else
+ if (length > roomof(16, SIZEOF_BDIGIT)) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into 'signed 128-bit integer'");
+ }
+ BDIGIT *digits = BIGNUM_DIGITS(x);
+ rb_uint128_t unsigned_result = {0};
+ rb_uint128_from_bignum_digits_fallback(&unsigned_result, digits, length);
+ if (BIGNUM_NEGATIVE_P(x)) {
+ // Check if value fits in signed 128-bit (max negative is 2^127)
+ uint64_t max_neg_high = (uint64_t)1 << 63;
+ if (unsigned_result.parts.high > max_neg_high || (unsigned_result.parts.high == max_neg_high && unsigned_result.parts.low > 0)) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into 'signed 128-bit integer'");
+ }
+ // Convert from absolute value to two's complement (Ruby stores negative as absolute value)
+ rb_uint128_twos_complement_negate(&unsigned_result);
+ result.parts.low = unsigned_result.parts.low;
+ result.parts.high = (int64_t)unsigned_result.parts.high; // Sign extend
+ }
+ else {
+ // Check if value fits in signed 128-bit (max positive is 2^127 - 1)
+ // Max positive: high = 0x7FFFFFFFFFFFFFFF, low = 0xFFFFFFFFFFFFFFFF
+ uint64_t max_pos_high = ((uint64_t)1 << 63) - 1;
+ if (unsigned_result.parts.high > max_pos_high) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into 'signed 128-bit integer'");
+ }
+ result.parts.low = unsigned_result.parts.low;
+ result.parts.high = unsigned_result.parts.high;
+ }
+#endif
+ return result;
+ }
+ else {
+ rb_raise(rb_eTypeError, "not an integer");
+ }
+}
+
+VALUE
+rb_uint128_to_numeric(rb_uint128_t n)
+{
+#ifdef HAVE_UINT128_T
+ if (n.value <= (uint128_t)RUBY_FIXNUM_MAX) {
+ return LONG2FIX((long)n.value);
+ }
+ return rb_uint128t2big(n.value);
+#else
+ // If high part is zero and low part fits in fixnum
+ if (n.parts.high == 0 && n.parts.low <= (uint64_t)RUBY_FIXNUM_MAX) {
+ return LONG2FIX((long)n.parts.low);
+ }
+ // Convert to bignum by building it from the two 64-bit parts
+ VALUE bignum = rb_ull2big(n.parts.low);
+ if (n.parts.high > 0) {
+ VALUE high_bignum = rb_ull2big(n.parts.high);
+ // Multiply high part by 2^64 and add to low part
+ VALUE shifted_value = rb_int_lshift(high_bignum, INT2FIX(64));
+ bignum = rb_int_plus(bignum, shifted_value);
+ }
+ return bignum;
+#endif
+}
+
+VALUE
+rb_int128_to_numeric(rb_int128_t n)
+{
+#ifdef HAVE_UINT128_T
+ if (FIXABLE(n.value)) {
+ return LONG2FIX((long)n.value);
+ }
+ return rb_int128t2big(n.value);
+#else
+ int64_t high = (int64_t)n.parts.high;
+ // If it's a small positive value that fits in fixnum
+ if (high == 0 && n.parts.low <= (uint64_t)RUBY_FIXNUM_MAX) {
+ return LONG2FIX((long)n.parts.low);
+ }
+ // Check if it's negative (high bit of high part is set)
+ if (high < 0) {
+ // Negative value - convert from two's complement to absolute value
+ rb_uint128_t unsigned_value = {0};
+ if (n.parts.low == 0) {
+ unsigned_value.parts.low = 0;
+ unsigned_value.parts.high = ~n.parts.high + 1;
+ }
+ else {
+ unsigned_value.parts.low = ~n.parts.low + 1;
+ unsigned_value.parts.high = ~n.parts.high + (unsigned_value.parts.low == 0 ? 1 : 0);
+ }
+ VALUE bignum = rb_uint128_to_numeric(unsigned_value);
+ return rb_int_uminus(bignum);
+ }
+ else {
+ // Positive value
+ union uint128_int128_conversion conversion = {
+ .int128 = n
+ };
+ return rb_uint128_to_numeric(conversion.uint128);
+ }
+#endif
+}
+
/********************************************************************
*
* Document-class: Integer
@@ -3539,14 +3670,14 @@ rb_num2ull(VALUE val)
*
* You can convert certain objects to Integers with:
*
- * - \Method #Integer.
+ * - Method #Integer.
*
* An attempt to add a singleton method to an instance of this class
* causes an exception to be raised.
*
* == What's Here
*
- * First, what's elsewhere. \Class \Integer:
+ * First, what's elsewhere. Class \Integer:
*
* - Inherits from
* {class Numeric}[rdoc-ref:Numeric@What-27s+Here]
@@ -3592,6 +3723,7 @@ rb_num2ull(VALUE val)
* - #>>: Returns the value of +self+ after a rightward bit-shift.
* - #[]: Returns a slice of bits from +self+.
* - #^: Returns the bitwise EXCLUSIVE OR of +self+ and the given value.
+ * - #|: Returns the bitwise OR of +self+ and the given value.
* - #ceil: Returns the smallest number greater than or equal to +self+.
* - #chr: Returns a 1-character string containing the character
* represented by the value of +self+.
@@ -3611,7 +3743,6 @@ rb_num2ull(VALUE val)
* - #to_s (aliased as #inspect): Returns a string containing the place-value
* representation of +self+ in the given radix.
* - #truncate: Returns +self+ truncated to the given precision.
- * - #|: Returns the bitwise OR of +self+ and the given value.
*
* === Other
*
@@ -4030,17 +4161,20 @@ rb_fix_plus(VALUE x, VALUE y)
/*
* call-seq:
- * self + numeric -> numeric_result
+ * self + other -> numeric
+ *
+ * Returns the sum of +self+ and +other+:
*
- * Performs addition:
+ * 1 + 1 # => 2
+ * 1 + -1 # => 0
+ * 1 + 0 # => 1
+ * 1 + -2 # => -1
+ * 1 + Complex(1, 0) # => (2+0i)
+ * 1 + Rational(1, 1) # => (2/1)
*
- * 2 + 2 # => 4
- * -2 + 2 # => 0
- * -2 + -2 # => -4
- * 2 + 2.0 # => 4.0
- * 2 + Rational(2, 1) # => (4/1)
- * 2 + Complex(2, 0) # => (4+0i)
+ * For a computation involving Floats, the result may be inexact (see Float#+):
*
+ * 1 + 3.14 # => 4.140000000000001
*/
VALUE
@@ -4075,9 +4209,9 @@ fix_minus(VALUE x, VALUE y)
/*
* call-seq:
- * self - numeric -> numeric_result
+ * self - other -> numeric
*
- * Performs subtraction:
+ * Returns the difference of +self+ and +other+:
*
* 4 - 2 # => 2
* -4 - 2 # => -6
@@ -4131,16 +4265,17 @@ fix_mul(VALUE x, VALUE y)
/*
* call-seq:
- * self * numeric -> numeric_result
+ * self * other -> numeric
*
- * Performs multiplication:
+ * Returns the numeric product of +self+ and +other+:
*
* 4 * 2 # => 8
- * 4 * -2 # => -8
* -4 * 2 # => -8
+ * 4 * -2 # => -8
* 4 * 2.0 # => 8.0
* 4 * Rational(1, 3) # => (4/3)
* 4 * Complex(2, 0) # => (8+0i)
+ *
*/
VALUE
@@ -4263,16 +4398,18 @@ fix_div(VALUE x, VALUE y)
/*
* call-seq:
- * self / numeric -> numeric_result
+ * self / other -> numeric
+ *
+ * Returns the quotient of +self+ and +other+.
*
- * Performs division; for integer +numeric+, truncates the result to an integer:
+ * For integer +other+, truncates the result to an integer:
*
* 4 / 3 # => 1
* 4 / -3 # => -2
* -4 / 3 # => -2
* -4 / -3 # => 1
*
- * For other +numeric+, returns non-integer result:
+ * For non-integer +other+, returns a non-integer result:
*
* 4 / 3.0 # => 1.3333333333333333
* 4 / Rational(3, 1) # => (4/3)
@@ -4349,9 +4486,9 @@ fix_mod(VALUE x, VALUE y)
/*
* call-seq:
- * self % other -> real_number
+ * self % other -> real_numeric
*
- * Returns +self+ modulo +other+ as a real number.
+ * Returns +self+ modulo +other+ as a real numeric (\Integer, \Float, or \Rational).
*
* For integer +n+ and real number +r+, these expressions are equivalent:
*
@@ -4500,9 +4637,9 @@ rb_int_divmod(VALUE x, VALUE y)
/*
* call-seq:
- * self ** numeric -> numeric_result
+ * self ** exponent -> numeric
*
- * Raises +self+ to the power of +numeric+:
+ * Returns +self+ raised to the power +exponent+:
*
* 2 ** 3 # => 8
* 2 ** -3 # => (1/8)
@@ -4623,17 +4760,47 @@ fix_pow(VALUE x, VALUE y)
/*
* call-seq:
- * self ** numeric -> numeric_result
- *
- * Raises +self+ to the power of +numeric+:
- *
- * 2 ** 3 # => 8
- * 2 ** -3 # => (1/8)
- * -2 ** 3 # => -8
- * -2 ** -3 # => (-1/8)
- * 2 ** 3.3 # => 9.849155306759329
- * 2 ** Rational(3, 1) # => (8/1)
- * 2 ** Complex(3, 0) # => (8+0i)
+ * self ** exponent -> numeric
+ *
+ * Returns +self+ raised to the power +exponent+:
+ *
+ * # Result for non-negative Integer exponent is Integer.
+ * 2 ** 0 # => 1
+ * 2 ** 1 # => 2
+ * 2 ** 2 # => 4
+ * 2 ** 3 # => 8
+ * -2 ** 3 # => -8
+ * # Result for negative Integer exponent is Rational, not Float.
+ * 2 ** -3 # => (1/8)
+ * -2 ** -3 # => (-1/8)
+ *
+ * # Result for Float exponent is Float.
+ * 2 ** 0.0 # => 1.0
+ * 2 ** 1.0 # => 2.0
+ * 2 ** 2.0 # => 4.0
+ * 2 ** 3.0 # => 8.0
+ * -2 ** 3.0 # => -8.0
+ * 2 ** -3.0 # => 0.125
+ * -2 ** -3.0 # => -0.125
+ *
+ * # Result for non-negative Complex exponent is Complex with Integer parts.
+ * 2 ** Complex(0, 0) # => (1+0i)
+ * 2 ** Complex(1, 0) # => (2+0i)
+ * 2 ** Complex(2, 0) # => (4+0i)
+ * 2 ** Complex(3, 0) # => (8+0i)
+ * -2 ** Complex(3, 0) # => (-8+0i)
+ * # Result for negative Complex exponent is Complex with Rational parts.
+ * 2 ** Complex(-3, 0) # => ((1/8)+(0/1)*i)
+ * -2 ** Complex(-3, 0) # => ((-1/8)+(0/1)*i)
+ *
+ * # Result for Rational exponent is Rational.
+ * 2 ** Rational(0, 1) # => (1/1)
+ * 2 ** Rational(1, 1) # => (2/1)
+ * 2 ** Rational(2, 1) # => (4/1)
+ * 2 ** Rational(3, 1) # => (8/1)
+ * -2 ** Rational(3, 1) # => (-8/1)
+ * 2 ** Rational(-3, 1) # => (1/8)
+ * -2 ** Rational(-3, 1) # => (-1/8)
*
*/
VALUE
@@ -4686,7 +4853,7 @@ fix_equal(VALUE x, VALUE y)
* call-seq:
* self == other -> true or false
*
- * Returns +true+ if +self+ is numerically equal to +other+; +false+ otherwise.
+ * Returns whether +self+ is numerically equal to +other+:
*
* 1 == 2 #=> false
* 1 == 1.0 #=> true
@@ -4732,28 +4899,29 @@ fix_cmp(VALUE x, VALUE y)
/*
* call-seq:
- * self <=> other -> -1, 0, +1, or nil
+ * self <=> other -> -1, 0, 1, or nil
+ *
+ * Compares +self+ and +other+.
*
* Returns:
*
- * - -1, if +self+ is less than +other+.
- * - 0, if +self+ is equal to +other+.
- * - 1, if +self+ is greater then +other+.
+ * - +-1+, if +self+ is less than +other+.
+ * - +0+, if +self+ is equal to +other+.
+ * - +1+, if +self+ is greater then +other+.
* - +nil+, if +self+ and +other+ are incomparable.
*
* Examples:
*
* 1 <=> 2 # => -1
* 1 <=> 1 # => 0
- * 1 <=> 0 # => 1
- * 1 <=> 'foo' # => nil
- *
* 1 <=> 1.0 # => 0
* 1 <=> Rational(1, 1) # => 0
* 1 <=> Complex(1, 0) # => 0
+ * 1 <=> 0 # => 1
+ * 1 <=> 'foo' # => nil
*
- * This method is the basis for comparisons in module Comparable.
- *
+ * \Class \Integer includes module Comparable,
+ * each of whose methods uses Integer#<=> for comparison.
*/
VALUE
@@ -4791,7 +4959,8 @@ fix_gt(VALUE x, VALUE y)
* call-seq:
* self > other -> true or false
*
- * Returns +true+ if the value of +self+ is greater than that of +other+:
+ * Returns whether the value of +self+ is greater than the value of +other+;
+ * +other+ must be numeric, but may not be Complex:
*
* 1 > 0 # => true
* 1 > 1 # => false
@@ -4835,10 +5004,10 @@ fix_ge(VALUE x, VALUE y)
/*
* call-seq:
- * self >= real -> true or false
+ * self >= other -> true or false
*
- * Returns +true+ if the value of +self+ is greater than or equal to
- * that of +other+:
+ * Returns whether the value of +self+ is greater than or equal to the value of +other+;
+ * +other+ must be numeric, but may not be Complex:
*
* 1 >= 0 # => true
* 1 >= 1 # => true
@@ -4883,7 +5052,8 @@ fix_lt(VALUE x, VALUE y)
* call-seq:
* self < other -> true or false
*
- * Returns +true+ if the value of +self+ is less than that of +other+:
+ * Returns whether the value of +self+ is less than the value of +other+;
+ * +other+ must be numeric, but may not be Complex:
*
* 1 < 0 # => false
* 1 < 1 # => false
@@ -4891,8 +5061,6 @@ fix_lt(VALUE x, VALUE y)
* 1 < 0.5 # => false
* 1 < Rational(1, 2) # => false
*
- * Raises an exception if the comparison cannot be made.
- *
*/
static VALUE
@@ -4927,10 +5095,10 @@ fix_le(VALUE x, VALUE y)
/*
* call-seq:
- * self <= real -> true or false
+ * self <= other -> true or false
*
- * Returns +true+ if the value of +self+ is less than or equal to
- * that of +other+:
+ * Returns whether the value of +self+ is less than or equal to the value of +other+;
+ * +other+ must be numeric, but may not be Complex:
*
* 1 <= 0 # => false
* 1 <= 1 # => true
@@ -5115,8 +5283,8 @@ fix_xor(VALUE x, VALUE y)
*
*/
-static VALUE
-int_xor(VALUE x, VALUE y)
+VALUE
+rb_int_xor(VALUE x, VALUE y)
{
if (FIXNUM_P(x)) {
return fix_xor(x, y);
@@ -5289,9 +5457,22 @@ generate_mask(VALUE len)
}
static VALUE
+int_aref2(VALUE num, VALUE beg, VALUE len)
+{
+ if (RB_TYPE_P(num, T_BIGNUM)) {
+ return rb_big_aref2(num, beg, len);
+ }
+ else {
+ num = rb_int_rshift(num, beg);
+ VALUE mask = generate_mask(len);
+ return rb_int_and(num, mask);
+ }
+}
+
+static VALUE
int_aref1(VALUE num, VALUE arg)
{
- VALUE orig_num = num, beg, end;
+ VALUE beg, end;
int excl;
if (rb_range_values(arg, &beg, &end, &excl)) {
@@ -5311,22 +5492,19 @@ int_aref1(VALUE num, VALUE arg)
return INT2FIX(0);
}
}
- num = rb_int_rshift(num, beg);
int cmp = compare_indexes(beg, end);
if (!NIL_P(end) && cmp < 0) {
VALUE len = rb_int_minus(end, beg);
if (!excl) len = rb_int_plus(len, INT2FIX(1));
- VALUE mask = generate_mask(len);
- num = rb_int_and(num, mask);
+ return int_aref2(num, beg, len);
}
else if (cmp == 0) {
if (excl) return INT2FIX(0);
- num = orig_num;
arg = beg;
goto one_bit;
}
- return num;
+ return rb_int_rshift(num, beg);
}
one_bit:
@@ -5339,15 +5517,6 @@ one_bit:
return Qnil;
}
-static VALUE
-int_aref2(VALUE num, VALUE beg, VALUE len)
-{
- num = rb_int_rshift(num, beg);
- VALUE mask = generate_mask(len);
- num = rb_int_and(num, mask);
- return num;
-}
-
/*
* call-seq:
* self[offset] -> 0 or 1
@@ -5552,7 +5721,7 @@ rb_int_digits_bigbase(VALUE num, VALUE base)
}
bases = rb_ary_new();
- for (VALUE b = base; int_lt(b, num) == Qtrue; b = rb_int_mul(b, b)) {
+ for (VALUE b = base; int_le(b, num) == Qtrue; b = rb_int_mul(b, b)) {
rb_ary_push(bases, b);
}
digits = rb_ary_new_from_args(1, num);
@@ -5978,7 +6147,11 @@ prefix##_isqrt(argtype n) \
while ((t = n/x) < (argtype)x) x = (rettype)((x + t) >> 1); \
return x; \
} \
- return (rettype)sqrt(argtype##_TO_DOUBLE(n)); \
+ rettype x = (rettype)sqrt(argtype##_TO_DOUBLE(n)); \
+ /* libm sqrt may returns a larger approximation than actual. */ \
+ /* Our isqrt always returns a smaller approximation. */ \
+ if (x * x > n) x--; \
+ return x; \
}
#if SIZEOF_LONG*CHAR_BIT > DBL_MANT_DIG
@@ -6190,7 +6363,7 @@ int_s_try_convert(VALUE self, VALUE num)
*
* == What's Here
*
- * First, what's elsewhere. \Class \Numeric:
+ * First, what's elsewhere. Class \Numeric:
*
* - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
* - Includes {module Comparable}[rdoc-ref:Comparable@What-27s+Here].
@@ -6362,7 +6535,7 @@ Init_Numeric(void)
rb_define_method(rb_cInteger, "&", rb_int_and, 1);
rb_define_method(rb_cInteger, "|", int_or, 1);
- rb_define_method(rb_cInteger, "^", int_xor, 1);
+ rb_define_method(rb_cInteger, "^", rb_int_xor, 1);
rb_define_method(rb_cInteger, "[]", int_aref, -1);
rb_define_method(rb_cInteger, "<<", rb_int_lshift, 1);
@@ -6450,7 +6623,7 @@ Init_Numeric(void)
*
* If the platform supports denormalized numbers,
* there are numbers between zero and Float::MIN.
- * 0.0.next_float returns the smallest positive floating point number
+ * +0.0.next_float+ returns the smallest positive floating point number
* including denormalized numbers.
*/
rb_define_const(rb_cFloat, "MIN", DBL2NUM(DBL_MIN));