Skip to content

Commit 0c36eee

Browse files
committed
Fix x**y, x.power(y, 0) and x.sqrt(0) calculates huge digits if precision limit is huge
1 parent 2d932f4 commit 0c36eee

File tree

2 files changed

+37
-11
lines changed

2 files changed

+37
-11
lines changed

lib/bigdecimal.rb

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,10 @@ def power(y, prec = 0)
144144
return BigDecimal(1)
145145
end
146146

147-
prec = BigDecimal.limit if prec.zero?
147+
limit = BigDecimal.limit
148148
frac_part = y.frac
149149

150-
if frac_part.zero? && prec.zero?
150+
if frac_part.zero? && prec.zero? && limit.zero?
151151
# Infinite precision calculation for `x ** int` and `x.power(int)`
152152
int_part = y.fix.to_i
153153
int_part = -int_part if (neg = int_part < 0)
@@ -167,18 +167,19 @@ def power(y, prec = 0)
167167
return neg ? BigDecimal(1) / ans : ans
168168
end
169169

170-
prec = [x.n_significant_digits, y.n_significant_digits, BigDecimal.double_fig].max + BigDecimal.double_fig if prec.zero?
170+
result_prec = prec.nonzero? || [x.n_significant_digits, y.n_significant_digits, BigDecimal.double_fig].max + BigDecimal.double_fig
171+
result_prec = [result_prec, limit].min if prec.zero? && limit.nonzero?
172+
173+
prec2 = result_prec + BigDecimal.double_fig
171174

172175
if y < 0
173-
inv = x.power(-y, prec)
176+
inv = x.power(-y, prec2)
174177
return BigDecimal(0) if inv.infinite?
175178
return BigDecimal::Internal.infinity_computation_result if inv.zero?
176-
return BigDecimal(1).div(inv, prec)
179+
return BigDecimal(1).div(inv, result_prec)
177180
end
178181

179-
prec2 = prec + BigDecimal.double_fig
180-
181-
if frac_part.zero? && y.exponent < Math.log(prec) * 5 + 20
182+
if frac_part.zero? && y.exponent < Math.log(result_prec) * 5 + 20
182183
# Use exponentiation by squaring if y is an integer and not too large
183184
pow_prec = prec2 + y.exponent
184185
n = 1
@@ -191,7 +192,7 @@ def power(y, prec = 0)
191192
break if n > int_part
192193
xn = xn.mult(xn, pow_prec)
193194
end
194-
ans.mult(1, prec)
195+
ans.mult(1, result_prec)
195196
else
196197
if x > 1
197198
# To calculate exp(z, prec), z needs prec+max(z.exponent, 0) precision if z > 0.
@@ -200,7 +201,7 @@ def power(y, prec = 0)
200201
ylogx_exponent = y.exponent + logx_exponent
201202
prec2 += [ylogx_exponent, 0].max
202203
end
203-
BigMath.exp(BigMath.log(x, prec2).mult(y, prec2), prec)
204+
BigMath.exp(BigMath.log(x, prec2).mult(y, prec2), result_prec)
204205
end
205206
end
206207

@@ -217,7 +218,9 @@ def sqrt(prec)
217218
return self if zero?
218219

219220
if prec == 0
220-
prec = BigDecimal.limit.nonzero? || n_significant_digits + BigDecimal.double_fig
221+
limit = BigDecimal.limit
222+
prec = n_significant_digits + BigDecimal.double_fig
223+
prec = [limit, prec].min if limit.nonzero?
221224
end
222225

223226
ex = exponent / 2

test/bigdecimal/test_bigdecimal.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2072,14 +2072,17 @@ def test_arithmetic_operation_with_limit
20722072
assert_equal(BigDecimal('0.889'), (BigDecimal('0.8888') - BigDecimal('0')))
20732073
assert_equal(BigDecimal('2.66'), (BigDecimal('0.888') * BigDecimal('3')))
20742074
assert_equal(BigDecimal('0.296'), (BigDecimal('0.8888') / BigDecimal('3')))
2075+
assert_equal(BigDecimal('0.222e109'), BigDecimal('98.76') ** BigDecimal('54.32'))
20752076
assert_equal(BigDecimal('0.889'), BigDecimal('0.8888').add(BigDecimal('0'), 0))
20762077
assert_equal(BigDecimal('0.889'), BigDecimal('0.8888').sub(BigDecimal('0'), 0))
20772078
assert_equal(BigDecimal('2.66'), BigDecimal('0.888').mult(BigDecimal('3'), 0))
20782079
assert_equal(BigDecimal('0.296'), BigDecimal('0.8888').div(BigDecimal('3'), 0))
2080+
assert_equal(BigDecimal('0.222e109'), BigDecimal('98.76').power(BigDecimal('54.32'), 0))
20792081
assert_equal(BigDecimal('0.8888'), BigDecimal('0.8888').add(BigDecimal('0'), 5))
20802082
assert_equal(BigDecimal('0.8888'), BigDecimal('0.8888').sub(BigDecimal('0'), 5))
20812083
assert_equal(BigDecimal('2.664'), BigDecimal('0.888').mult(BigDecimal('3'), 5))
20822084
assert_equal(BigDecimal('0.29627'), BigDecimal('0.8888').div(BigDecimal('3'), 5))
2085+
assert_equal(BigDecimal('0.22164e109'), BigDecimal('98.76').power(BigDecimal('54.32'), 5))
20832086
end
20842087
end
20852088

@@ -2092,6 +2095,26 @@ def test_div_with_huge_limit
20922095
end
20932096
end
20942097

2098+
def test_sqrt_with_huge_limit
2099+
BigDecimal.save_limit do
2100+
x = BigDecimal("12.34")
2101+
sqrt = x.sqrt(0)
2102+
BigDecimal.limit(1000)
2103+
assert_equal(sqrt, x.sqrt(0))
2104+
end
2105+
end
2106+
2107+
def test_power_with_huge_limit
2108+
BigDecimal.save_limit do
2109+
x = BigDecimal("12.34")
2110+
y = BigDecimal("56.78")
2111+
pow = x ** y
2112+
BigDecimal.limit(1000)
2113+
assert_equal(pow, x ** y)
2114+
assert_equal(pow, x.power(y, 0))
2115+
end
2116+
end
2117+
20952118
def test_div_mod_rem_operation_with_limit
20962119
x = -(9 ** 100)
20972120
y = 7 ** 100

0 commit comments

Comments
 (0)