Skip to content

Commit

Permalink
bigdecimal.c: Fix special cases of BigDecimal#**
Browse files Browse the repository at this point in the history
[Bug #18677]
  • Loading branch information
mrkn committed Apr 20, 2022
1 parent f6b058e commit 0297be9
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 13 deletions.
28 changes: 18 additions & 10 deletions ext/bigdecimal/bigdecimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -2769,7 +2769,14 @@ bigdecimal_power_by_bigdecimal(Real const* x, Real const* exp, ssize_t const n)
VALUE log_x, multiplied, y;
volatile VALUE obj = exp->obj;

if (VpIsZero(exp)) {
if (VpIsNaN(exp)) {
Real *nan = VpCreateRbObject(n, "0", true);
RB_GC_GUARD(nan->obj);
VpSetNaN(nan);
return VpCheckGetValue(nan);
}

if (VpIsZero(exp) || VpIsPosOne(x)) {
return VpCheckGetValue(VpCreateRbObject(n, "1", true));
}

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

case T_DATA:
if (is_kind_of_BigDecimal(vexp)) {
VALUE zero = INT2FIX(0);
VALUE rounded = BigDecimal_round(1, &zero, vexp);
if (RTEST(BigDecimal_eq(vexp, rounded))) {
vexp = BigDecimal_to_i(vexp);
goto retry;
}
if (NIL_P(prec)) {
GUARD_OBJ(y, GetVpValue(vexp, 1));
n += y->Prec*VpBaseFig();
if (BigDecimal_IsFinite(vexp)) {
VALUE rounded = BigDecimal_round(0, NULL, vexp);
if (RTEST(BigDecimal_eq(vexp, rounded))) {
vexp = BigDecimal_to_i(vexp);
goto retry;
}
if (NIL_P(prec)) {
GUARD_OBJ(y, GetVpValue(vexp, 1));
n += y->Prec*VpBaseFig();
}
}
exp = DATA_PTR(vexp);
break;
Expand Down
1 change: 1 addition & 0 deletions ext/bigdecimal/bigdecimal.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ VP_EXPORT Real *VpOne(void);
#define VpSetInf(a,s) (void)(((s)>0)?VpSetPosInf(a):VpSetNegInf(a))
#define VpHasVal(a) (a->frac[0])
#define VpIsOne(a) ((a->Prec==1)&&(a->frac[0]==1)&&(a->exponent==1))
#define VpIsPosOne(a) (((a)->sign>0)&&VpIsOne(a))
#define VpExponent(a) (a->exponent)
#ifdef BIGDECIMAL_DEBUG
int VpVarCheck(Real * v);
Expand Down
46 changes: 43 additions & 3 deletions test/bigdecimal/test_bigdecimal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,20 @@ def test_round_up
assert_equal(-4, m3h.round)
end

def test_round_positive_infinity
BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
assert_nothing_raised do
assert_positive_infinite(BigDecimal::INFINITY.round)
end
end

def test_round_negative_infinity
BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
assert_nothing_raised do
assert_negative_infinite((-BigDecimal::INFINITY).round)
end
end

def test_zero_p
BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
Expand Down Expand Up @@ -1492,9 +1506,35 @@ def test_power_with_Bignum
end
end

def test_power_with_BigDecimal
assert_nothing_raised do
assert_in_delta(3 ** 3, BigDecimal(3) ** BigDecimal(3))
data(
"BigDecimal(3) ** BigDecimal(3) -> BigDecimal(3**3)" => [BigDecimal(3), BigDecimal(3), BigDecimal(3**3)],
"BigDecimal(10) ** Inf -> Inf" => [BigDecimal(10), BigDecimal::INFINITY, :pos_inf],
"BigDecimal(10) ** NaN -> NaN" => [BigDecimal(10), BigDecimal::NAN, :nan],
"Inf ** BigDecimal(0) -> BigDecimal(1)" => [BigDecimal::INFINITY, BigDecimal(0), BigDecimal(1)],
"-Inf ** BigDecimal(0) -> BigDecimal(-1)" => [-BigDecimal::INFINITY, BigDecimal(0), BigDecimal(1)],
"BigDecimal(1) ** Inf -> 1" => [BigDecimal(1), BigDecimal::INFINITY, BigDecimal(1)],
"BigDecimal(1) ** -Inf -> 1" => [BigDecimal(1), -BigDecimal::INFINITY, BigDecimal(1)],
"BigDecimal(0) ** Inf -> 0" => [BigDecimal(0), BigDecimal::INFINITY, BigDecimal(0)],
"BigDecimal(0) ** -Inf -> Inf" => [BigDecimal(0), -BigDecimal::INFINITY, :pos_inf],
"BigDecimal(-1) ** Inf -> Math::DomainError" => [BigDecimal(-1), BigDecimal::INFINITY, :math_domain_error],
"BigDecimal(-1) ** -Inf -> Math::DomainError" => [BigDecimal(-1), -BigDecimal::INFINITY, :math_domain_error]
)
def test_power_with_BigDecimal(data)
BigDecimal.save_exception_mode do
BigDecimal.mode(BigDecimal::EXCEPTION_ALL, false)
x, y, res = *data
case res
when :pos_inf
assert_nothing_raised { assert_positive_infinite(x ** y) }
when :neg_inf
assert_nothing_raised { assert_negative_infinite(x ** y) }
when :nan
assert_nothing_raised { assert_nan(x ** y) }
when :math_domain_error
assert_raise(Math::DomainError) { x ** y }
else
assert_nothing_raised { assert_in_delta(res, x ** y) }
end
end
end

Expand Down

0 comments on commit 0297be9

Please sign in to comment.