Skip to content

Commit 45cd4a8

Browse files
committed
Do not round a**b to infinity
... instead, just calculate the value unless it is too big. Also, this change raises an ArgumentError if it is expected to exceed 16 GB in a 64-bit environment. (It is possible to calculate it straightforward, but it would likely be out-of-memory, so I didn't think it would make sense.) [Feature #20811]
1 parent f7b334e commit 45cd4a8

File tree

7 files changed

+120
-59
lines changed

7 files changed

+120
-59
lines changed

bignum.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6287,8 +6287,7 @@ rb_big_pow(VALUE x, VALUE y)
62876287
y = bignorm(y);
62886288
if (FIXNUM_P(y))
62896289
goto again;
6290-
rb_warn("in a**b, b may be too big");
6291-
d = rb_big2dbl(y);
6290+
rb_raise(rb_eArgError, "exponent is too large");
62926291
}
62936292
else if (FIXNUM_P(y)) {
62946293
yy = FIX2LONG(y);
@@ -6304,13 +6303,16 @@ rb_big_pow(VALUE x, VALUE y)
63046303
VALUE z = 0;
63056304
SIGNED_VALUE mask;
63066305
const size_t xbits = rb_absint_numwords(x, 1, NULL);
6307-
const size_t BIGLEN_LIMIT = 32*1024*1024;
6306+
#if SIZEOF_SIZE_T == 4
6307+
const size_t BIGLEN_LIMIT = 1ULL << 31; // 2 GB
6308+
#else // SIZEOF_SIZE_T == 8
6309+
const size_t BIGLEN_LIMIT = 1ULL << 34; // 16 GB
6310+
#endif
63086311

63096312
if (xbits == (size_t)-1 ||
63106313
(xbits > BIGLEN_LIMIT) ||
63116314
(xbits * yy > BIGLEN_LIMIT)) {
6312-
rb_warn("in a**b, b may be too big");
6313-
d = (double)yy;
6315+
rb_raise(rb_eArgError, "exponent is too large");
63146316
}
63156317
else {
63166318
for (mask = FIXNUM_MAX + 1; mask; mask >>= 1) {

rational.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,8 +1048,7 @@ rb_rational_pow(VALUE self, VALUE other)
10481048
}
10491049
}
10501050
else if (RB_BIGNUM_TYPE_P(other)) {
1051-
rb_warn("in a**b, b may be too big");
1052-
return rb_float_pow(nurat_to_f(self), other);
1051+
rb_raise(rb_eArgError, "exponent is too large");
10531052
}
10541053
else if (RB_FLOAT_TYPE_P(other) || RB_TYPE_P(other, T_RATIONAL)) {
10551054
return rb_float_pow(nurat_to_f(self), other);

spec/ruby/core/integer/shared/exponent.rb

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,18 @@
4848
(-1).send(@method, 4611686018427387905).should eql(-1)
4949
end
5050

51-
it "returns Float::INFINITY when the number is too big" do
52-
-> {
53-
2.send(@method, 427387904).should == Float::INFINITY
54-
}.should complain(/warning: in a\*\*b, b may be too big/)
51+
ruby_version_is ""..."3.4" do
52+
it "returns Float::INFINITY when the number is too big" do
53+
-> {
54+
2.send(@method, 427387904).should == Float::INFINITY
55+
}.should complain(/warning: in a\*\*b, b may be too big/)
56+
end
57+
end
58+
59+
ruby_version_is "3.4" do
60+
it "raises an ArgumentError when the number is too big" do
61+
-> { 100000000.send(@method, 1000000000) }.should raise_error(ArgumentError)
62+
end
5563
end
5664

5765
it "raises a ZeroDivisionError for 0 ** -1" do
@@ -108,13 +116,23 @@
108116
-> { @bignum.send(@method, :symbol) }.should raise_error(TypeError)
109117
end
110118

111-
it "switch to a Float when the values is too big" do
112-
flt = nil
113-
-> {
114-
flt = @bignum.send(@method, @bignum)
115-
}.should complain(/warning: in a\*\*b, b may be too big/)
116-
flt.should be_kind_of(Float)
117-
flt.infinite?.should == 1
119+
ruby_version_is ""..."3.4" do
120+
it "switch to a Float when the values is too big" do
121+
flt = nil
122+
-> {
123+
flt = @bignum.send(@method, @bignum)
124+
}.should complain(/warning: in a\*\*b, b may be too big/)
125+
flt.should be_kind_of(Float)
126+
flt.infinite?.should == 1
127+
end
128+
end
129+
130+
ruby_version_is "3.4" do
131+
it "does not switch to a Float when the values is too big" do
132+
-> {
133+
@bignum.send(@method, @bignum)
134+
}.should raise_error(ArgumentError)
135+
end
118136
end
119137

120138
it "returns a complex number when negative and raised to a fractional power" do

spec/ruby/shared/rational/exponent.rb

Lines changed: 73 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -84,47 +84,91 @@
8484
(Rational(-1) ** bignum_value(3)).should eql(Rational(-1))
8585
end
8686

87-
it "returns positive Infinity when self is > 1" do
88-
-> {
89-
(Rational(2) ** bignum_value).infinite?.should == 1
90-
}.should complain(/warning: in a\*\*b, b may be too big/)
91-
-> {
92-
(Rational(fixnum_max) ** bignum_value).infinite?.should == 1
93-
}.should complain(/warning: in a\*\*b, b may be too big/)
94-
end
95-
96-
it "returns 0.0 when self is > 1 and the exponent is negative" do
97-
-> {
98-
(Rational(2) ** -bignum_value).should eql(0.0)
99-
}.should complain(/warning: in a\*\*b, b may be too big/)
100-
-> {
101-
(Rational(fixnum_max) ** -bignum_value).should eql(0.0)
102-
}.should complain(/warning: in a\*\*b, b may be too big/)
103-
end
104-
105-
# Fails on linux due to pow() bugs in glibc: http://sources.redhat.com/bugzilla/show_bug.cgi?id=3866
106-
platform_is_not :linux do
107-
it "returns positive Infinity when self < -1" do
87+
ruby_version_is ""..."3.4" do
88+
it "returns positive Infinity when self is > 1" do
10889
-> {
109-
(Rational(-2) ** bignum_value).infinite?.should == 1
90+
(Rational(2) ** bignum_value).infinite?.should == 1
11091
}.should complain(/warning: in a\*\*b, b may be too big/)
11192
-> {
112-
(Rational(-2) ** (bignum_value + 1)).infinite?.should == 1
113-
}.should complain(/warning: in a\*\*b, b may be too big/)
114-
-> {
115-
(Rational(fixnum_min) ** bignum_value).infinite?.should == 1
93+
(Rational(fixnum_max) ** bignum_value).infinite?.should == 1
11694
}.should complain(/warning: in a\*\*b, b may be too big/)
11795
end
11896

119-
it "returns 0.0 when self is < -1 and the exponent is negative" do
97+
it "returns 0.0 when self is > 1 and the exponent is negative" do
12098
-> {
121-
(Rational(-2) ** -bignum_value).should eql(0.0)
99+
(Rational(2) ** -bignum_value).should eql(0.0)
122100
}.should complain(/warning: in a\*\*b, b may be too big/)
123101
-> {
124-
(Rational(fixnum_min) ** -bignum_value).should eql(0.0)
102+
(Rational(fixnum_max) ** -bignum_value).should eql(0.0)
125103
}.should complain(/warning: in a\*\*b, b may be too big/)
126104
end
127105
end
106+
107+
ruby_version_is "3.4" do
108+
it "raises an ArgumentError when self is > 1" do
109+
-> {
110+
(Rational(2) ** bignum_value)
111+
}.should raise_error(ArgumentError)
112+
-> {
113+
(Rational(fixnum_max) ** bignum_value)
114+
}.should raise_error(ArgumentError)
115+
end
116+
117+
it "raises an ArgumentError when self is > 1 and the exponent is negative" do
118+
-> {
119+
(Rational(2) ** -bignum_value)
120+
}.should raise_error(ArgumentError)
121+
-> {
122+
(Rational(fixnum_max) ** -bignum_value)
123+
}.should raise_error(ArgumentError)
124+
end
125+
end
126+
127+
# Fails on linux due to pow() bugs in glibc: http://sources.redhat.com/bugzilla/show_bug.cgi?id=3866
128+
platform_is_not :linux do
129+
ruby_version_is ""..."3.4" do
130+
it "returns positive Infinity when self < -1" do
131+
-> {
132+
(Rational(-2) ** bignum_value).infinite?.should == 1
133+
}.should complain(/warning: in a\*\*b, b may be too big/)
134+
-> {
135+
(Rational(-2) ** (bignum_value + 1)).infinite?.should == 1
136+
}.should complain(/warning: in a\*\*b, b may be too big/)
137+
-> {
138+
(Rational(fixnum_min) ** bignum_value).infinite?.should == 1
139+
}.should complain(/warning: in a\*\*b, b may be too big/)
140+
end
141+
142+
it "returns 0.0 when self is < -1 and the exponent is negative" do
143+
-> {
144+
(Rational(-2) ** -bignum_value).should eql(0.0)
145+
}.should complain(/warning: in a\*\*b, b may be too big/)
146+
-> {
147+
(Rational(fixnum_min) ** -bignum_value).should eql(0.0)
148+
}.should complain(/warning: in a\*\*b, b may be too big/)
149+
end
150+
end
151+
152+
ruby_version_is "3.4" do
153+
it "returns positive Infinity when self < -1" do
154+
-> {
155+
(Rational(-2) ** bignum_value)
156+
}.should raise_error(ArgumentError)
157+
-> {
158+
(Rational(fixnum_min) ** bignum_value)
159+
}.should raise_error(ArgumentError)
160+
end
161+
162+
it "returns 0.0 when self is < -1 and the exponent is negative" do
163+
-> {
164+
(Rational(-2) ** -bignum_value)
165+
}.should raise_error(ArgumentError)
166+
-> {
167+
(Rational(fixnum_min) ** -bignum_value)
168+
}.should raise_error(ArgumentError)
169+
end
170+
end
171+
end
128172
end
129173

130174
describe "when passed Float" do

test/ruby/test_bignum.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,8 +476,8 @@ def test_quo
476476
def test_pow
477477
assert_equal(1.0, T32 ** 0.0)
478478
assert_equal(1.0 / T32, T32 ** -1)
479-
assert_equal(1, assert_warning(/may be too big/) {T32 ** T32}.infinite?)
480-
assert_equal(1, assert_warning(/may be too big/) {T32 ** (2**30-1)}.infinite?)
479+
assert_raise(ArgumentError) { T32 ** T32 }
480+
assert_raise(ArgumentError) { T32 ** (2**30-1) }
481481

482482
### rational changes the behavior of Bignum#**
483483
#assert_raise(TypeError) { T32**"foo" }

test/ruby/test_integer.rb

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,20 +57,19 @@ def test_pow
5757
nil
5858
end, "[ruby-dev:32084] [ruby-dev:34547]")
5959

60-
x = EnvUtil.suppress_warning {2 ** -0x4000000000000000}
61-
assert_in_delta(0.0, (x / 2), Float::EPSILON)
60+
assert_raise(ArgumentError) {2 ** -0x4000000000000000}
6261

6362
<<~EXPRS.each_line.with_index(__LINE__+1) do |expr, line|
6463
crash01: 111r+11**-11111161111111
6564
crash02: 1118111111111**-1111111111111111**1+1==11111
66-
crash03: -1111111**-1111*11 - -1111111** -111111111
65+
crash03: -1111111**-1111*11 - -11** -11111111
6766
crash04: 1118111111111** -1111111111111111**1+11111111111**1 ===111
6867
crash05: 11** -111155555555555555 -55 !=5-555
6968
crash07: 1 + 111111111**-1111811111
7069
crash08: 18111111111**-1111111111111111**1 + 1111111111**-1111**1
7170
crash10: -7 - -1111111** -1111**11
7271
crash12: 1118111111111** -1111111111111111**1 + 1111 - -1111111** -1111*111111111119
73-
crash13: 1.0i - -1111111** -111111111
72+
crash13: 1.0i - -11** -11111111
7473
crash14: 11111**111111111**111111 * -11111111111111111111**-111111111111
7574
crash15: ~1**1111 + -~1**~1**111
7675
crash17: 11** -1111111**1111 /11i
@@ -80,7 +79,7 @@ def test_pow
8079
crash21: 11**-10111111119-1i -1r
8180
EXPRS
8281
name, expr = expr.split(':', 2)
83-
assert_ruby_status(%w"-W0", expr, name)
82+
assert_ruby_status(%w"-W0", "begin; #{ expr }; rescue ArgumentError; end", name)
8483
end
8584
end
8685

test/ruby/test_rational.rb

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,11 +1066,10 @@ def test_power_of_0
10661066
end
10671067

10681068
def test_power_overflow
1069-
bug = '[ruby-core:79686] [Bug #13242]: Infinity due to overflow'
1070-
x = EnvUtil.suppress_warning {4r**40000000}
1071-
assert_predicate x, :infinite?, bug
1072-
x = EnvUtil.suppress_warning {(1/4r)**40000000}
1073-
assert_equal 0, x, bug
1069+
assert_raise(ArgumentError) { 4r**400000000000000000000 }
1070+
exp = 4**40000000
1071+
assert_equal exp, 4r**40000000
1072+
assert_equal 1r/exp, (1/4r)**40000000
10741073
end
10751074

10761075
def test_positive_p

0 commit comments

Comments
 (0)