Skip to content

Commit d7f7d0d

Browse files
committed
bigdecimal.c: Fix special cases of BigDecimal#**
[Bug #18677]
1 parent f6b058e commit d7f7d0d

File tree

3 files changed

+48
-13
lines changed

3 files changed

+48
-13
lines changed

ext/bigdecimal/bigdecimal.c

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2769,7 +2769,14 @@ bigdecimal_power_by_bigdecimal(Real const* x, Real const* exp, ssize_t const n)
27692769
VALUE log_x, multiplied, y;
27702770
volatile VALUE obj = exp->obj;
27712771

2772-
if (VpIsZero(exp)) {
2772+
if (VpIsNaN(exp)) {
2773+
Real *nan = VpCreateRbObject(n, "0", true);
2774+
RB_GC_GUARD(nan->obj);
2775+
VpSetNaN(nan);
2776+
return VpCheckGetValue(nan);
2777+
}
2778+
2779+
if (VpIsZero(exp) || VpIsPosOne(x)) {
27732780
return VpCheckGetValue(VpCreateRbObject(n, "1", true));
27742781
}
27752782

@@ -2858,15 +2865,16 @@ BigDecimal_power(int argc, VALUE*argv, VALUE self)
28582865

28592866
case T_DATA:
28602867
if (is_kind_of_BigDecimal(vexp)) {
2861-
VALUE zero = INT2FIX(0);
2862-
VALUE rounded = BigDecimal_round(1, &zero, vexp);
2863-
if (RTEST(BigDecimal_eq(vexp, rounded))) {
2864-
vexp = BigDecimal_to_i(vexp);
2865-
goto retry;
2866-
}
2867-
if (NIL_P(prec)) {
2868-
GUARD_OBJ(y, GetVpValue(vexp, 1));
2869-
n += y->Prec*VpBaseFig();
2868+
if (BigDecimal_IsFinite(vexp)) {
2869+
VALUE rounded = BigDecimal_round(0, NULL, vexp);
2870+
if (RTEST(BigDecimal_eq(vexp, rounded))) {
2871+
vexp = BigDecimal_to_i(vexp);
2872+
goto retry;
2873+
}
2874+
if (NIL_P(prec)) {
2875+
GUARD_OBJ(y, GetVpValue(vexp, 1));
2876+
n += y->Prec*VpBaseFig();
2877+
}
28702878
}
28712879
exp = DATA_PTR(vexp);
28722880
break;

ext/bigdecimal/bigdecimal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ VP_EXPORT Real *VpOne(void);
283283
#define VpSetInf(a,s) (void)(((s)>0)?VpSetPosInf(a):VpSetNegInf(a))
284284
#define VpHasVal(a) (a->frac[0])
285285
#define VpIsOne(a) ((a->Prec==1)&&(a->frac[0]==1)&&(a->exponent==1))
286+
#define VpIsPosOne(a) (((a)->sign>0)&&VpIsOne(a))
286287
#define VpExponent(a) (a->exponent)
287288
#ifdef BIGDECIMAL_DEBUG
288289
int VpVarCheck(Real * v);

test/bigdecimal/test_bigdecimal.rb

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,9 +1492,35 @@ def test_power_with_Bignum
14921492
end
14931493
end
14941494

1495-
def test_power_with_BigDecimal
1496-
assert_nothing_raised do
1497-
assert_in_delta(3 ** 3, BigDecimal(3) ** BigDecimal(3))
1495+
data(
1496+
"BigDecimal(3) ** BigDecimal(3) -> BigDecimal(3**3)" => [BigDecimal(3), BigDecimal(3), BigDecimal(3**3)],
1497+
"BigDecimal(10) ** Inf -> Inf" => [BigDecimal(10), BigDecimal::INFINITY, :pos_inf],
1498+
"BigDecimal(10) ** NaN -> NaN" => [BigDecimal(10), BigDecimal::NAN, :nan],
1499+
"Inf ** BigDecimal(0) -> BigDecimal(1)" => [BigDecimal::INFINITY, BigDecimal(0), BigDecimal(1)],
1500+
"-Inf ** BigDecimal(0) -> BigDecimal(-1)" => [-BigDecimal::INFINITY, BigDecimal(0), BigDecimal(1)],
1501+
"BigDecimal(1) ** Inf -> 1" => [BigDecimal(1), BigDecimal::INFINITY, BigDecimal(1)],
1502+
"BigDecimal(1) ** -Inf -> 1" => [BigDecimal(1), -BigDecimal::INFINITY, BigDecimal(1)],
1503+
"BigDecimal(0) ** Inf -> 0" => [BigDecimal(0), BigDecimal::INFINITY, BigDecimal(0)],
1504+
"BigDecimal(0) ** -Inf -> Inf" => [BigDecimal(0), -BigDecimal::INFINITY, :pos_inf],
1505+
"BigDecimal(-1) ** Inf -> Math::DomainError" => [BigDecimal(-1), BigDecimal::INFINITY, :math_domain_error],
1506+
"BigDecimal(-1) ** -Inf -> Math::DomainError" => [BigDecimal(-1), -BigDecimal::INFINITY, :math_domain_error]
1507+
)
1508+
def test_power_with_BigDecimal(data)
1509+
BigDecimal.save_exception_mode do
1510+
BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false)
1511+
x, y, res = *data
1512+
case res
1513+
when :pos_inf
1514+
assert_nothing_raised { assert_positive_infinite(x ** y) }
1515+
when :neg_inf
1516+
assert_nothing_raised { assert_negative_infinite(x ** y) }
1517+
when :nan
1518+
assert_nothing_raised { assert_nan(x ** y) }
1519+
when :math_domain_error
1520+
assert_raise(Math::DomainError) { x ** y }
1521+
else
1522+
assert_nothing_raised { assert_in_delta(res, x ** y) }
1523+
end
14981524
end
14991525
end
15001526

0 commit comments

Comments
 (0)