Skip to content

Commit f7e6d44

Browse files
committed
[GR-14962] 26 numeric specs.
PullRequest: truffleruby/684
2 parents 58e2729 + 2124a90 commit f7e6d44

31 files changed

+90
-93
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
Bug fixes:
44

55
* Sharing for thread-safety of objects is now triggered later as intended, e.g., when a second `Thread` is started.
6+
Compatibility:
7+
8+
* Exceptions from `coerce` are no longer rescued, like MRI.
9+
* Implemented `Integer#{allbits?,anybits?,nobits?}`.
10+
* `Integer#{ceil,floor,truncate}` now accept a precision and `Integer#round` accepts a rounding mode.
611

712
# 20.0.0 beta 1
813

spec/ruby/core/integer/round_spec.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,15 @@
6767
25.round(-1, half: :up).should eql(30)
6868
25.round(-1, half: :down).should eql(20)
6969
25.round(-1, half: :even).should eql(20)
70+
25.round(-1, half: nil).should eql(30)
7071
35.round(-1, half: :up).should eql(40)
7172
35.round(-1, half: :down).should eql(30)
7273
35.round(-1, half: :even).should eql(40)
74+
35.round(-1, half: nil).should eql(40)
7375
(-25).round(-1, half: :up).should eql(-30)
7476
(-25).round(-1, half: :down).should eql(-20)
7577
(-25).round(-1, half: :even).should eql(-20)
78+
(-25).round(-1, half: nil).should eql(-30)
7679
end
7780

7881
ruby_version_is "2.4"..."2.5" do
@@ -90,4 +93,9 @@
9093
35.round(1, half: :even).should eql(35)
9194
end
9295
end
96+
97+
it "raises ArgumentError for an unknown rounding mode" do
98+
lambda { 42.round(-1, half: :foo) }.should raise_error(ArgumentError, /invalid rounding mode: foo/)
99+
lambda { 42.round(1, half: :foo) }.should raise_error(ArgumentError, /invalid rounding mode: foo/)
100+
end
93101
end

spec/tags/core/float/divide_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/float/gt_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/float/gte_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/float/lt_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/float/lte_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/float/minus_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/float/multiply_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/float/plus_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/integer/allbits_tags.txt

Lines changed: 0 additions & 4 deletions
This file was deleted.

spec/tags/core/integer/anybits_tags.txt

Lines changed: 0 additions & 4 deletions
This file was deleted.

spec/tags/core/integer/ceil_tags.txt

Lines changed: 0 additions & 4 deletions
This file was deleted.

spec/tags/core/integer/comparison_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/integer/divide_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/integer/exponent_tags.txt

Lines changed: 0 additions & 2 deletions
This file was deleted.

spec/tags/core/integer/floor_tags.txt

Lines changed: 0 additions & 4 deletions
This file was deleted.

spec/tags/core/integer/gt_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/integer/gte_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/integer/lt_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/integer/lte_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/integer/minus_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/integer/multiply_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/integer/nobits_tags.txt

Lines changed: 0 additions & 4 deletions
This file was deleted.

spec/tags/core/integer/plus_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/tags/core/integer/pow_tags.txt

Lines changed: 0 additions & 23 deletions
This file was deleted.

spec/tags/core/integer/round_tags.txt

Lines changed: 0 additions & 4 deletions
This file was deleted.

spec/tags/core/integer/truncate_tags.txt

Lines changed: 0 additions & 4 deletions
This file was deleted.

src/main/java/org/truffleruby/core/numeric/IntegerNodes.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,17 +1742,23 @@ public Object pow(DynamicObject a, long b,
17421742
}
17431743

17441744
@Specialization
1745-
public double pow(DynamicObject a, double b) {
1746-
return powBigIntegerDouble(Layouts.BIGNUM.getValue(a), b);
1745+
public Object pow(DynamicObject a, double b) {
1746+
double doublePow = powBigIntegerDouble(Layouts.BIGNUM.getValue(a), b);
1747+
if (Double.isNaN(doublePow)) {
1748+
// Instead of returning NaN, run the fallback code which can create a complex result
1749+
return FAILURE;
1750+
} else {
1751+
return doublePow;
1752+
}
17471753
}
17481754

17491755
@Specialization(guards = "isRubyBignum(b)")
1750-
public Void pow(DynamicObject a, DynamicObject b) {
1751-
throw new UnsupportedOperationException();
1756+
public Object pow(DynamicObject a, DynamicObject b) {
1757+
return FAILURE;
17521758
}
17531759

17541760
@Specialization(guards = { "!isInteger(b)", "!isLong(b)", "!isDouble(b)", "!isRubyBignum(b)" })
1755-
public Object pow(long a, Object b) {
1761+
public Object pow(Object a, Object b) {
17561762
return FAILURE;
17571763
}
17581764

src/main/ruby/truffleruby/core/integer.rb

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,18 @@
3939

4040
class Integer < Numeric
4141

42-
alias_method :truncate, :to_i
43-
alias_method :ceil, :to_i
44-
alias_method :floor, :to_i
45-
4642
# Have a copy in Integer of the Numeric version, as MRI does
4743
public :remainder
4844

4945
def **(o)
5046
Truffle.primitive :integer_pow
5147

52-
if o.is_a?(Float) && self < 0 && o != o.round
48+
if (o.is_a?(Float) || o.is_a?(Rational)) && self < 0 && o != o.round
5349
return Complex.new(self, 0) ** o
5450
elsif o.is_a?(Integer) && o < 0
5551
return Rational.new(self, 1) ** o
52+
elsif o.is_a?(Integer) && o > 0
53+
return self ** o.to_f
5654
end
5755

5856
redo_coerced :**, o
@@ -63,6 +61,22 @@ def [](index)
6361
index < 0 ? 0 : (self >> index) & 1
6462
end
6563

64+
def allbits?(mask)
65+
mask = Truffle::Type.coerce_to_int(mask)
66+
(self & mask) == mask
67+
end
68+
69+
def anybits?(mask)
70+
mask = Truffle::Type.coerce_to_int(mask)
71+
(self & mask) != 0
72+
end
73+
74+
def ceil(precision = 0)
75+
return self unless precision < 0
76+
x = 10 ** precision.abs
77+
((self / x) + 1) * x
78+
end
79+
6680
def coerce(other)
6781
if other.kind_of? Integer
6882
return [other, self]
@@ -88,6 +102,23 @@ def fdiv(n)
88102
end
89103
end
90104

105+
def floor(precision = 0)
106+
return self unless precision < 0
107+
x = 10 ** precision.abs
108+
(self / x) * x
109+
end
110+
111+
def nobits?(mask)
112+
mask = Truffle::Type.coerce_to_int(mask)
113+
(self & mask) == 0
114+
end
115+
116+
def pow(e, m=undefined)
117+
return self ** e if undefined.equal?(m)
118+
raise TypeError, '2nd argument not allowed unless all arguments are integers' unless Truffle::Type.object_kind_of?(m, Integer)
119+
(self ** e) % m
120+
end
121+
91122
def times
92123
return to_enum(:times) { self } unless block_given?
93124

@@ -99,6 +130,14 @@ def times
99130
self
100131
end
101132

133+
def truncate(precision = 0)
134+
if precision >= 0
135+
self
136+
else
137+
round(precision, half: :down)
138+
end
139+
end
140+
102141
def chr(enc=undefined)
103142
if self < 0 || (self & 0xffff_ffff) != self
104143
raise RangeError, "#{self} is outside of the valid character range"
@@ -122,7 +161,7 @@ def chr(enc=undefined)
122161
String.from_codepoint self, enc
123162
end
124163

125-
def round(ndigits=undefined)
164+
def round(ndigits=undefined, half: :up)
126165
return self if undefined.equal? ndigits
127166

128167
if Float === ndigits && ndigits.infinite?
@@ -132,10 +171,13 @@ def round(ndigits=undefined)
132171
ndigits = Truffle::Type.coerce_to_int(ndigits)
133172
Truffle::Type.check_int(ndigits)
134173

135-
if ndigits > 0
136-
to_f
137-
elsif ndigits == 0
138-
self
174+
if ndigits >= 0
175+
case half
176+
when :up, nil, :down, :even
177+
self
178+
else
179+
raise ArgumentError, "invalid rounding mode: #{half}"
180+
end
139181
else
140182
ndigits = -ndigits
141183

@@ -148,7 +190,18 @@ def round(ndigits=undefined)
148190

149191
if kind_of? Integer and f.kind_of? Integer
150192
x = self < 0 ? -self : self
151-
x = (x + f / 2) / f * f
193+
case half
194+
when :up, nil
195+
x = (x + (f / 2)) / f
196+
when :down
197+
x = x / f
198+
when :even
199+
x = (x + (f / 2)) / f
200+
x = (x / 2) * 2
201+
else
202+
raise ArgumentError, "invalid rounding mode: #{half}"
203+
end
204+
x = x * f
152205
x = -x if self < 0
153206
return x
154207
end

src/main/ruby/truffleruby/core/numeric.rb

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -238,13 +238,7 @@ def math_coerce(other, error=:coerce_error)
238238
other = Truffle::Interop.unbox_if_needed(other)
239239
return math_coerce_error(other, error) unless other.respond_to? :coerce
240240

241-
begin
242-
values = other.coerce(self)
243-
rescue
244-
warn 'Numerical comparison operators will no more rescue exceptions of #coerce', uplevel: 1
245-
warn 'in the next release. Return nil in #coerce if the coercion is impossible.', uplevel: 1
246-
return math_coerce_error(other, error)
247-
end
241+
values = other.coerce(self)
248242

249243
unless Truffle::Type.object_kind_of?(values, Array) && values.length == 2
250244
if error == :no_error
@@ -286,6 +280,7 @@ def redo_coerced(meth, right, *extra)
286280

287281
def redo_compare(meth, right)
288282
b, a = math_coerce(right, :compare_error)
283+
return nil unless b
289284
a.__send__ meth, b
290285
end
291286
private :redo_compare

0 commit comments

Comments
 (0)