Skip to content

Commit 79712fc

Browse files
committed
[ruby/bigdecimal] Let BigDecimal#quo accept precision
Fix GH-214. ruby/bigdecimal@13e0e93f37
1 parent 0b8638c commit 79712fc

File tree

2 files changed

+96
-24
lines changed

2 files changed

+96
-24
lines changed

ext/bigdecimal/bigdecimal.c

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ static ID id_half;
115115
*/
116116
static unsigned short VpGetException(void);
117117
static void VpSetException(unsigned short f);
118+
static void VpCheckException(Real *p, bool always);
119+
static VALUE VpCheckGetValue(Real *p);
118120
static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v);
119121
static int VpLimitRound(Real *c, size_t ixDigit);
120122
static Real *VpCopy(Real *pv, Real const* const x);
@@ -165,27 +167,6 @@ is_kind_of_BigDecimal(VALUE const v)
165167
return rb_typeddata_is_kind_of(v, &BigDecimal_data_type);
166168
}
167169

168-
static void
169-
VpCheckException(Real *p, bool always)
170-
{
171-
if (VpIsNaN(p)) {
172-
VpException(VP_EXCEPTION_NaN, "Computation results in 'NaN' (Not a Number)", always);
173-
}
174-
else if (VpIsPosInf(p)) {
175-
VpException(VP_EXCEPTION_INFINITY, "Computation results in 'Infinity'", always);
176-
}
177-
else if (VpIsNegInf(p)) {
178-
VpException(VP_EXCEPTION_INFINITY, "Computation results in '-Infinity'", always);
179-
}
180-
}
181-
182-
static VALUE
183-
VpCheckGetValue(Real *p)
184-
{
185-
VpCheckException(p, false);
186-
return p->obj;
187-
}
188-
189170
NORETURN(static void cannot_be_coerced_into_BigDecimal(VALUE, VALUE));
190171

191172
static void
@@ -1656,12 +1637,15 @@ BigDecimal_divide(VALUE self, VALUE r, Real **c, Real **res, Real **div)
16561637
}
16571638

16581639
/* call-seq:
1659-
* a / b -> bigdecimal
1660-
* quo(value) -> bigdecimal
1640+
* a / b -> bigdecimal
16611641
*
16621642
* Divide by the specified value.
16631643
*
1644+
* The result precision will be the precision of the larger operand,
1645+
* but its minimum is 2*Float::DIG.
1646+
*
16641647
* See BigDecimal#div.
1648+
* See BigDecimal#quo.
16651649
*/
16661650
static VALUE
16671651
BigDecimal_div(VALUE self, VALUE r)
@@ -1683,6 +1667,45 @@ BigDecimal_div(VALUE self, VALUE r)
16831667
return VpCheckGetValue(c);
16841668
}
16851669

1670+
static VALUE BigDecimal_round(int argc, VALUE *argv, VALUE self);
1671+
1672+
/* call-seq:
1673+
* quo(value) -> bigdecimal
1674+
* quo(value, digits) -> bigdecimal
1675+
*
1676+
* Divide by the specified value.
1677+
*
1678+
* digits:: If specified and less than the number of significant digits of
1679+
* the result, the result is rounded to the given number of digits,
1680+
* according to the rounding mode indicated by BigDecimal.mode.
1681+
*
1682+
* If digits is 0 or omitted, the result is the same as for the
1683+
* / operator.
1684+
*
1685+
* See BigDecimal#/.
1686+
* See BigDecimal#div.
1687+
*/
1688+
static VALUE
1689+
BigDecimal_quo(int argc, VALUE *argv, VALUE self)
1690+
{
1691+
VALUE value, digits, result;
1692+
SIGNED_VALUE n = -1;
1693+
1694+
argc = rb_scan_args(argc, argv, "11", &value, &digits);
1695+
if (argc > 1) {
1696+
n = GetPrecisionInt(digits);
1697+
}
1698+
1699+
if (n > 0) {
1700+
result = BigDecimal_div2(self, value, digits);
1701+
}
1702+
else {
1703+
result = BigDecimal_div(self, value);
1704+
}
1705+
1706+
return result;
1707+
}
1708+
16861709
/*
16871710
* %: mod = a%b = a - (a.to_f/b).floor * b
16881711
* div = (a.to_f/b).floor
@@ -1964,6 +1987,7 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
19641987
* Document-method: BigDecimal#div
19651988
*
19661989
* call-seq:
1990+
* div(value) -> integer
19671991
* div(value, digits) -> bigdecimal or integer
19681992
*
19691993
* Divide by the specified value.
@@ -1978,6 +2002,9 @@ BigDecimal_div2(VALUE self, VALUE b, VALUE n)
19782002
* If digits is not specified, the result is an integer,
19792003
* by analogy with Float#div; see also BigDecimal#divmod.
19802004
*
2005+
* See BigDecimal#/.
2006+
* See BigDecimal#quo.
2007+
*
19812008
* Examples:
19822009
*
19832010
* a = BigDecimal("4")
@@ -4272,7 +4299,7 @@ Init_bigdecimal(void)
42724299
rb_define_method(rb_cBigDecimal, "-@", BigDecimal_neg, 0);
42734300
rb_define_method(rb_cBigDecimal, "*", BigDecimal_mult, 1);
42744301
rb_define_method(rb_cBigDecimal, "/", BigDecimal_div, 1);
4275-
rb_define_method(rb_cBigDecimal, "quo", BigDecimal_div, 1);
4302+
rb_define_method(rb_cBigDecimal, "quo", BigDecimal_quo, -1);
42764303
rb_define_method(rb_cBigDecimal, "%", BigDecimal_mod, 1);
42774304
rb_define_method(rb_cBigDecimal, "modulo", BigDecimal_mod, 1);
42784305
rb_define_method(rb_cBigDecimal, "remainder", BigDecimal_remainder, 1);
@@ -4446,6 +4473,27 @@ VpSetException(unsigned short f)
44464473
bigdecimal_set_thread_local_exception_mode(f);
44474474
}
44484475

4476+
static void
4477+
VpCheckException(Real *p, bool always)
4478+
{
4479+
if (VpIsNaN(p)) {
4480+
VpException(VP_EXCEPTION_NaN, "Computation results in 'NaN' (Not a Number)", always);
4481+
}
4482+
else if (VpIsPosInf(p)) {
4483+
VpException(VP_EXCEPTION_INFINITY, "Computation results in 'Infinity'", always);
4484+
}
4485+
else if (VpIsNegInf(p)) {
4486+
VpException(VP_EXCEPTION_INFINITY, "Computation results in '-Infinity'", always);
4487+
}
4488+
}
4489+
4490+
static VALUE
4491+
VpCheckGetValue(Real *p)
4492+
{
4493+
VpCheckException(p, false);
4494+
return p->obj;
4495+
}
4496+
44494497
/*
44504498
* Precision limit.
44514499
*/

test/bigdecimal/test_bigdecimal.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,30 @@ def test_div_bigdecimal_with_float_and_precision
11011101
x.div(y, 100))
11021102
end
11031103

1104+
def test_quo_without_prec
1105+
x = BigDecimal(5)
1106+
y = BigDecimal(229)
1107+
assert_equal(BigDecimal("0.021834061135371179039301310043668122"), x.quo(y))
1108+
end
1109+
1110+
def test_quo_with_prec
1111+
begin
1112+
saved_mode = BigDecimal.mode(BigDecimal::ROUND_MODE)
1113+
BigDecimal.mode(BigDecimal::ROUND_MODE, :half_up)
1114+
1115+
x = BigDecimal(5)
1116+
y = BigDecimal(229)
1117+
assert_equal(BigDecimal("0.021834061135371179039301310043668122"), x.quo(y, 0))
1118+
assert_equal(BigDecimal("0.022"), x.quo(y, 2))
1119+
assert_equal(BigDecimal("0.0218"), x.quo(y, 3))
1120+
assert_equal(BigDecimal("0.0218341"), x.quo(y, 6))
1121+
assert_equal(BigDecimal("0.02183406114"), x.quo(y, 10))
1122+
assert_equal(BigDecimal("0.021834061135371179039301310043668122270742358078603"), x.quo(y, 50))
1123+
ensure
1124+
BigDecimal.mode(BigDecimal::ROUND_MODE, saved_mode)
1125+
end
1126+
end
1127+
11041128
def test_abs_bigdecimal
11051129
x = BigDecimal((2**100).to_s)
11061130
assert_equal(1267650600228229401496703205376, x.abs)

0 commit comments

Comments
 (0)