Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix divison #240

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 48 additions & 21 deletions ext/bigdecimal/bigdecimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1855,22 +1855,37 @@ static VALUE BigDecimal_DoDivmod(VALUE self, VALUE r, Real **div, Real **mod);
*/
static VALUE
BigDecimal_div(VALUE self, VALUE r)
/* For c = self/r: with round operation */
{
ENTER(5);
Real *c=NULL, *res=NULL, *div = NULL;
r = BigDecimal_divide(self, r, &c, &res, &div);
if (!NIL_P(r)) return r; /* coerced by other */
SAVE(c); SAVE(res); SAVE(div);
/* a/b = c + r/b */
/* c xxxxx
r 00000yyyyy ==> (y/b)*BASE >= HALF_BASE
*/
/* Round */
if (VpHasVal(div)) { /* frac[0] must be zero for NaN,INF,Zero */
VpInternalRound(c, 0, c->frac[c->Prec-1], (DECDIG)(VpBaseVal() * (DECDIG_DBL)res->frac[0] / div->frac[0]));
VALUE rr = r;
if (is_kind_of_BigDecimal(rr)) {
/* do nothing */
}
return VpCheckGetValue(c);
else if (RB_INTEGER_TYPE_P(r)) {
rr = rb_inum_convert_to_BigDecimal(r, 0, true);
}
else if (RB_TYPE_P(r, T_FLOAT)) {
rr = rb_float_convert_to_BigDecimal(r, 0, true);
}
else if (RB_TYPE_P(r, T_RATIONAL)) {
Real *a;
TypedData_Get_Struct(self, Real, &BigDecimal_data_type, a);
rr = rb_rational_convert_to_BigDecimal(r, a->Prec*BASE_FIG, true);
}

if (!is_kind_of_BigDecimal(rr)) {
return DoSomeOne(self, r, '/');
}

ssize_t a_prec, b_prec;
BigDecimal_count_precision_and_scale(self, &a_prec, NULL);
BigDecimal_count_precision_and_scale(rr, &b_prec, NULL);

size_t n = a_prec + b_prec;
if (n < 2*BIGDECIMAL_DOUBLE_FIGURES) {
n = 2*BIGDECIMAL_DOUBLE_FIGURES;
}

return BigDecimal_div2(self, rr, SIZET2NUM(n));
}

static VALUE BigDecimal_round(int argc, VALUE *argv, VALUE self);
Expand Down Expand Up @@ -2148,7 +2163,7 @@ BigDecimal_divmod(VALUE self, VALUE r)
* Do the same manner as Float#div when n is nil.
* Do the same manner as BigDecimal#quo when n is 0.
*/
static inline VALUE
static VALUE
BigDecimal_div2(VALUE self, VALUE b, VALUE n)
{
ENTER(5);
Expand Down Expand Up @@ -7182,9 +7197,14 @@ VpSqrt(Real *y, Real *x)
}

/*
* Round relatively from the decimal point.
* f: rounding mode
* nf: digit location to round from the decimal point.
* Round y on the specified mode and the location that counted from
* the decimal point.
*
* @param y a BigDecimal number to be rounded
* @param f the rounding mode
* @param nf the decimal location of rounding
*
* @return 1 if rounding was performed, otherwise 0
*/
VP_EXPORT int
VpMidRound(Real *y, unsigned short f, ssize_t nf)
Expand Down Expand Up @@ -7348,11 +7368,18 @@ VpMidRound(Real *y, unsigned short f, ssize_t nf)
return 1;
}

VP_EXPORT int
VpLeftRound(Real *y, unsigned short f, ssize_t nf)
/*
* Round from the left hand side of the digits.
* Round y on the specified mode and the location that counted from
* the most significant digit in y->frac[0] including leading zeros.
*
* @param y a BigDecimal number to be rounded
* @param f the rounding mode
* @param nf the decimal location of rounding
*
* @return 1 if rounding was performed, otherwise 0
*/
VP_EXPORT int
VpLeftRound(Real *y, unsigned short f, ssize_t nf)
{
DECDIG v;
if (!VpHasVal(y)) return 0; /* Unable to round */
Expand Down
34 changes: 31 additions & 3 deletions test/bigdecimal/test_bigdecimal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -973,13 +973,41 @@ def test_div
assert_raise_with_message(FloatDomainError, "Computation results in '-Infinity'") { BigDecimal("-1") / 0 }
end

def test_div_rounding_mode
BigDecimal.mode(BigDecimal::ROUND_MODE, :half_up)
assert_equal BigDecimal("0.55555555555555555555555555555556"), BigDecimal(5) / 9
BigDecimal.mode(BigDecimal::ROUND_MODE, :half_down)
assert_equal BigDecimal("0.55555555555555555555555555555555"), BigDecimal(5) / 9
end

def test_div_gh220
# https://github.com/ruby/bigdecimal/issues/220
x = BigDecimal("1.0")
y = BigDecimal("3672577333.6608990499165058135986328125")
c = BigDecimal("0.272288343892592687909520102748926752911779209181321744700032723729015151607289998e-9")
c = BigDecimal("0.272288343892592687909520102748926752912e-9")
# c = BigDecimal("0.272288343892592687909520102748926752911779209181321744700032723729015151607289998e-9")
assert_equal(c, x / y, "[GH-220]")
end

def test_div_gh222
# https://github.com/ruby/bigdecimal/issues/222
divisor = BigDecimal("1.03")
one = divisor.coerce(1.0)[0]

x = one
results = 10.times.map { x /= divisor }

x = one
expected_results = 10.times.map do |i|
x = x.div(divisor, 100).round(results[i].precision, half: :up)
end

10.times do |i|
assert_operator results[i].precision, :<=, 119
assert_equal expected_results[i], results[i]
end
end

def test_div_precision
bug13754 = '[ruby-core:82107] [Bug #13754]'
a = BigDecimal('101')
Expand Down Expand Up @@ -1113,7 +1141,7 @@ def test_div_bigdecimal_with_float_and_precision
def test_quo_without_prec
x = BigDecimal(5)
y = BigDecimal(229)
assert_equal(BigDecimal("0.021834061135371179039301310043668122"), x.quo(y))
assert_equal(BigDecimal("0.021834061135371179039301310043668"), x.quo(y))
end

def test_quo_with_prec
Expand All @@ -1123,7 +1151,7 @@ def test_quo_with_prec

x = BigDecimal(5)
y = BigDecimal(229)
assert_equal(BigDecimal("0.021834061135371179039301310043668122"), x.quo(y, 0))
assert_equal(BigDecimal("0.021834061135371179039301310043668"), x.quo(y, 0))
assert_equal(BigDecimal("0.022"), x.quo(y, 2))
assert_equal(BigDecimal("0.0218"), x.quo(y, 3))
assert_equal(BigDecimal("0.0218341"), x.quo(y, 6))
Expand Down