From 1d19283b04f0ac51cd0a6fd8452849849184de46 Mon Sep 17 00:00:00 2001 From: tompng Date: Sat, 10 May 2025 02:54:17 +0900 Subject: [PATCH 1/4] Rewrite BigDecimal#sqrt in ruby with improved Newton's method --- ext/bigdecimal/bigdecimal.c | 251 +---------------------------- ext/bigdecimal/bigdecimal.h | 3 - lib/bigdecimal.rb | 38 ++++- test/bigdecimal/test_bigdecimal.rb | 28 +++- 4 files changed, 61 insertions(+), 259 deletions(-) diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 6c008fc1..70cc4196 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -196,19 +196,6 @@ rbd_allocate_struct_zero(int sign, size_t const digits) return real; } -// Only used in VpSqrt through BigDecimal_sqrt -MAYBE_UNUSED(static inline Real * rbd_allocate_struct_one_nolimit(int sign, size_t const digits)); -#define NewOneNolimit rbd_allocate_struct_one_nolimit -static inline Real * -rbd_allocate_struct_one_nolimit(int sign, size_t const digits) -{ - Real *real = rbd_allocate_struct_decimal_digits(digits); - VpSetOne(real); - if (sign < 0) - VpSetSign(real, VP_SIGN_NEGATIVE_FINITE); - return real; -} - /* * ================== Ruby Interface part ========================== */ @@ -281,20 +268,6 @@ rbd_allocate_struct_zero_wrap(int sign, size_t const digits) return (BDVALUE) { BigDecimal_wrap_struct(rb_cBigDecimal, real), real }; } -// Only used in BigDecimal_sqrt -MAYBE_UNUSED(static inline BDVALUE rbd_allocate_struct_zero_limited_wrap(int sign, size_t const digits)); -#define NewZeroWrapLimited rbd_allocate_struct_zero_limited_wrap -static inline BDVALUE -rbd_allocate_struct_zero_limited_wrap(int sign, size_t digits) -{ - size_t prec_limit = VpGetPrecLimit(); - if (prec_limit) { - prec_limit += 2 * BASE_FIG; /* 2 more digits for rounding and division */ - if (prec_limit < digits) digits = prec_limit; - } - return rbd_allocate_struct_zero_wrap(sign, digits); -} - static inline int is_kind_of_BigDecimal(VALUE const v) { @@ -2081,32 +2054,6 @@ BigDecimal_abs(VALUE self) return CheckGetValue(c); } -/* call-seq: - * sqrt(n) - * - * Returns the square root of the value. - * - * Result has at least n significant digits. - */ -static VALUE -BigDecimal_sqrt(VALUE self, VALUE nFig) -{ - BDVALUE c, a; - size_t mx, n; - - a = GetBDValueMust(self); - mx = a.real->Prec * (VpBaseFig() + 1); - - n = check_int_precision(nFig); - n += VpDblFig() + VpBaseFig(); - if (mx <= n) mx = n; - c = NewZeroWrapLimited(1, mx); - VpSqrt(c.real, a.real); - - RB_GC_GUARD(a.bigdecimal); - return CheckGetValue(c); -} - /* Return the integer part of the number, as a BigDecimal. */ static VALUE @@ -3594,7 +3541,6 @@ Init_bigdecimal(void) rb_define_method(rb_cBigDecimal, "dup", BigDecimal_clone, 0); rb_define_method(rb_cBigDecimal, "to_f", BigDecimal_to_f, 0); rb_define_method(rb_cBigDecimal, "abs", BigDecimal_abs, 0); - rb_define_method(rb_cBigDecimal, "sqrt", BigDecimal_sqrt, 1); rb_define_method(rb_cBigDecimal, "fix", BigDecimal_fix, 0); rb_define_method(rb_cBigDecimal, "round", BigDecimal_round, -1); rb_define_method(rb_cBigDecimal, "frac", BigDecimal_frac, 0); @@ -3666,9 +3612,6 @@ static int gfDebug = 1; /* Debug switch */ #endif /* BIGDECIMAL_DEBUG */ static Real *VpConstOne; /* constant 1.0 */ -static Real *VpConstPt5; /* constant 0.5 */ -#define maxnr 100UL /* Maximum iterations for calculating sqrt. */ - /* used in VpSqrt() */ enum op_sw { OP_SW_ADD = 1, /* + */ @@ -4073,12 +4016,6 @@ VpInit(DECDIG BaseVal) VpConstOne = NewZero(1, 1); VpSetOne(VpConstOne); - /* Const 0.5 */ - VpConstPt5 = NewZero(1, 1); - VpSetSign(VpConstPt5, 1); - VpConstPt5->exponent = 0; - VpConstPt5->frac[0] = 5*BASE1; - #ifdef BIGDECIMAL_DEBUG gnAlloc = 0; #endif /* BIGDECIMAL_DEBUG */ @@ -4883,7 +4820,6 @@ VpMult(Real *c, Real *a, Real *b) size_t ind_as, ind_ae, ind_bs; DECDIG carry; DECDIG_DBL s; - Real *w; if (!VpIsDefOP(c, a, b, OP_SW_MULT)) return 0; /* No significant digit */ @@ -4903,29 +4839,19 @@ VpMult(Real *c, Real *a, Real *b) } if (b->Prec > a->Prec) { /* Adjust so that digits(a)>digits(b) */ - w = a; + Real *w = a; a = b; b = w; } - w = NULL; MxIndA = a->Prec - 1; MxIndB = b->Prec - 1; MxIndAB = a->Prec + b->Prec - 1; - // Only VpSqrt calls VpMult with insufficient precision - if (c->MaxPrec < VPMULT_RESULT_PREC(a, b)) { - w = c; - c = NewZero(1, VPMULT_RESULT_PREC(a, b) * BASE_FIG); - } - /* set LHSV c info */ c->exponent = a->exponent; /* set exponent */ VpSetSign(c, VpGetSign(a) * VpGetSign(b)); /* set sign */ - if (!AddExponent(c, b->exponent)) { - if (w) rbd_free_struct(c); - return 0; - } + if (!AddExponent(c, b->exponent)) return 0; carry = 0; nc = ind_c = MxIndAB; memset(c->frac, 0, (nc + 1) * sizeof(DECDIG)); /* Initialize c */ @@ -4973,11 +4899,6 @@ VpMult(Real *c, Real *a, Real *b) } } VpNmlz(c); - if (w != NULL) { /* free work variable */ - VpAsgn(w, c, 10); - rbd_free_struct(c); - c = w; - } Exit: return c->Prec*BASE_FIG; @@ -5879,174 +5800,6 @@ VpVtoD(double *d, SIGNED_VALUE *e, Real *m) return f; } -/* - * m <- d - */ -VP_EXPORT void -VpDtoV(Real *m, double d) -{ - size_t ind_m, mm; - SIGNED_VALUE ne; - DECDIG i; - double val, val2; - - if (isnan(d)) { - VpSetNaN(m); - goto Exit; - } - if (isinf(d)) { - if (d > 0.0) VpSetPosInf(m); - else VpSetNegInf(m); - goto Exit; - } - - if (d == 0.0) { - VpSetZero(m, 1); - goto Exit; - } - val = (d > 0.) ? d : -d; - ne = 0; - if (val >= 1.0) { - while (val >= 1.0) { - val /= (double)BASE; - ++ne; - } - } - else { - val2 = 1.0 / (double)BASE; - while (val < val2) { - val *= (double)BASE; - --ne; - } - } - /* Now val = 0.xxxxx*BASE**ne */ - - mm = m->MaxPrec; - memset(m->frac, 0, mm * sizeof(DECDIG)); - for (ind_m = 0; val > 0.0 && ind_m < mm; ind_m++) { - val *= (double)BASE; - i = (DECDIG)val; - val -= (double)i; - m->frac[ind_m] = i; - } - if (ind_m >= mm) ind_m = mm - 1; - VpSetSign(m, (d > 0.0) ? 1 : -1); - m->Prec = ind_m + 1; - m->exponent = ne; - - VpInternalRound(m, 0, (m->Prec > 0) ? m->frac[m->Prec-1] : 0, - (DECDIG)(val*(double)BASE)); - -Exit: - return; -} - -/* - * y = SQRT(x), y*y - x =>0 - */ -VP_EXPORT int -VpSqrt(Real *y, Real *x) -{ - Real *f = NULL; - Real *r = NULL; - size_t y_prec; - SIGNED_VALUE n, e; - ssize_t nr; - double val; - - /* Zero or +Infinity ? */ - if (VpIsZero(x) || VpIsPosInf(x)) { - VpAsgn(y,x,1); - goto Exit; - } - - /* Negative ? */ - if (BIGDECIMAL_NEGATIVE_P(x)) { - VpSetNaN(y); - return VpException(VP_EXCEPTION_OP, "sqrt of negative value", 0); - } - - /* NaN ? */ - if (VpIsNaN(x)) { - VpSetNaN(y); - return VpException(VP_EXCEPTION_OP, "sqrt of 'NaN'(Not a Number)", 0); - } - - /* One ? */ - if (VpIsOne(x)) { - VpSetOne(y); - goto Exit; - } - - n = (SIGNED_VALUE)y->MaxPrec; - if (x->MaxPrec > (size_t)n) n = (ssize_t)x->MaxPrec; - - /* allocate temporally variables */ - /* TODO: reconsider MaxPrec of f and r */ - f = NewOneNolimit(1, y->MaxPrec * (BASE_FIG + 2)); - r = NewOneNolimit(1, (n + n) * (BASE_FIG + 2)); - - nr = 0; - y_prec = y->MaxPrec; - - VpVtoD(&val, &e, x); /* val <- x */ - e /= (SIGNED_VALUE)BASE_FIG; - n = e / 2; - if (e - n * 2 != 0) { - val /= BASE; - n = (e + 1) / 2; - } - VpDtoV(y, sqrt(val)); /* y <- sqrt(val) */ - y->exponent += n; - n = (SIGNED_VALUE)roomof(BIGDECIMAL_DOUBLE_FIGURES, BASE_FIG); - y->MaxPrec = Min((size_t)n , y_prec); - f->MaxPrec = y->MaxPrec + 1; - n = (SIGNED_VALUE)(y_prec * BASE_FIG); - if (n > (SIGNED_VALUE)maxnr) n = (SIGNED_VALUE)maxnr; - - /* - * Perform: y_{n+1} = (y_n - x/y_n) / 2 - */ - do { - y->MaxPrec *= 2; - if (y->MaxPrec > y_prec) y->MaxPrec = y_prec; - f->MaxPrec = y->MaxPrec; - VpDivd(f, r, x, y); /* f = x/y */ - VpAddSub(r, f, y, -1); /* r = f - y */ - VpMult(f, VpConstPt5, r); /* f = 0.5*r */ - if (y_prec == y->MaxPrec && VpIsZero(f)) - goto converge; - VpAddSub(r, f, y, 1); /* r = y + f */ - VpAsgn(y, r, 1); /* y = r */ - } while (++nr < n); - -#ifdef BIGDECIMAL_DEBUG - if (gfDebug) { - printf("ERROR(VpSqrt): did not converge within %ld iterations.\n", nr); - } -#endif /* BIGDECIMAL_DEBUG */ - y->MaxPrec = y_prec; - -converge: - VpChangeSign(y, 1); -#ifdef BIGDECIMAL_DEBUG - if (gfDebug) { - VpMult(r, y, y); - VpAddSub(f, x, r, -1); - printf("VpSqrt: iterations = %"PRIdSIZE"\n", nr); - VPrint(stdout, " y =% \n", y); - VPrint(stdout, " x =% \n", x); - VPrint(stdout, " x-y*y = % \n", f); - } -#endif /* BIGDECIMAL_DEBUG */ - y->MaxPrec = y_prec; - -Exit: - rbd_free_struct(f); - rbd_free_struct(r); - return 1; -} - /* * Round relatively from the decimal point. * f: rounding mode diff --git a/ext/bigdecimal/bigdecimal.h b/ext/bigdecimal/bigdecimal.h index 9441c56d..82c88a2a 100644 --- a/ext/bigdecimal/bigdecimal.h +++ b/ext/bigdecimal/bigdecimal.h @@ -195,7 +195,6 @@ typedef struct { */ #define VpBaseFig() BIGDECIMAL_COMPONENT_FIGURES -#define VpDblFig() BIGDECIMAL_DOUBLE_FIGURES /* Zero,Inf,NaN (isinf(),isnan() used to check) */ VP_EXPORT double VpGetDoubleNaN(void); @@ -229,8 +228,6 @@ VP_EXPORT void VpToString(Real *a, char *buf, size_t bufsize, size_t fFmt, int f VP_EXPORT void VpToFString(Real *a, char *buf, size_t bufsize, size_t fFmt, int fPlus); VP_EXPORT int VpCtoV(Real *a, const char *int_chr, size_t ni, const char *frac, size_t nf, const char *exp_chr, size_t ne); VP_EXPORT int VpVtoD(double *d, SIGNED_VALUE *e, Real *m); -VP_EXPORT void VpDtoV(Real *m,double d); -VP_EXPORT int VpSqrt(Real *y,Real *x); VP_EXPORT int VpActiveRound(Real *y, Real *x, unsigned short f, ssize_t il); VP_EXPORT int VpMidRound(Real *y, unsigned short f, ssize_t nf); VP_EXPORT int VpLeftRound(Real *y, unsigned short f, ssize_t nf); diff --git a/lib/bigdecimal.rb b/lib/bigdecimal.rb index 68fe1677..4de8192e 100644 --- a/lib/bigdecimal.rb +++ b/lib/bigdecimal.rb @@ -172,6 +172,37 @@ def power(y, prec = nil) end ans.mult(1, prec) end + + # Returns the square root of the value. + # + # Result has at least prec significant digits. + # + def sqrt(prec) + if infinite? == 1 + exception_mode = BigDecimal.mode(BigDecimal::EXCEPTION_ALL) + raise FloatDomainError, "Computation results in 'Infinity'" if exception_mode.anybits?(BigDecimal::EXCEPTION_INFINITY) + return INFINITY + end + raise ArgumentError, 'negative precision' if prec < 0 + raise FloatDomainError, 'sqrt of negative value' if self < 0 + raise FloatDomainError, "sqrt of 'NaN'(Not a Number)" if nan? + return self if zero? + + # BigDecimal#sqrt calculates at least n_significant_digits precision. + # This feature maybe problematic for some cases. + n_digits = n_significant_digits + prec = [prec, n_digits].max + + ex = exponent / 2 + x = self.mult(BigDecimal("1e#{-ex * 2}"), n_significant_digits) + y = BigDecimal(Math.sqrt(x.to_f)) + precs = [prec + BigDecimal.double_fig] + precs << 2 + precs.last / 2 while precs.last > BigDecimal.double_fig + precs.reverse_each do |p| + y = y.add(x.div(y, p), p).div(2, p) + end + y.mult(BigDecimal("1e#{ex}"), precs.first) + end end # Core BigMath methods for BigDecimal (log, exp) are defined here. @@ -217,12 +248,7 @@ def self.log(x, prec) prec += BigDecimal.double_fig # log(x) = log(sqrt(sqrt(sqrt(sqrt(x))))) * 2**sqrt_steps - sqrt_steps = [2 * Integer.sqrt(prec) + 3 * x_minus_one_exponent, 0].max - - # Reduce sqrt_step until sqrt gets fast - # https://github.com/ruby/bigdecimal/pull/323 - # https://github.com/ruby/bigdecimal/pull/343 - sqrt_steps /= 10 + sqrt_steps = [Integer.sqrt(prec) + 3 * x_minus_one_exponent, 0].max lg2 = 0.3010299956639812 prec2 = prec + [-x_minus_one_exponent, 0].max + (sqrt_steps * lg2).ceil diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index 78ead2a8..46c15a9c 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -1452,6 +1452,16 @@ def test_sqrt_bigdecimal assert_equal(0, BigDecimal("-0").sqrt(1)) assert_equal(1, BigDecimal("1").sqrt(1)) assert_positive_infinite(BigDecimal("Infinity").sqrt(1)) + + # Out of float range + assert_equal(BigDecimal('12e1024'), BigDecimal('144e2048').sqrt(10)) + assert_equal(BigDecimal('12e-1024'), BigDecimal('144e-2048').sqrt(10)) + + sqrt2_300 = BigDecimal(2).sqrt(300) + (250..270).each do |prec| + sqrt_prec = prec + BigDecimal.double_fig - 1 + assert_in_delta(sqrt2_300, BigDecimal(2).sqrt(prec), BigDecimal("1e#{-sqrt_prec}")) + end end def test_sqrt_5266 @@ -1468,6 +1478,20 @@ def test_sqrt_5266 x.sqrt(109).to_s(109).split(' ')[0]) end + def test_sqrt_minimum_precision + x = BigDecimal((2**200).to_s) + assert_equal(2**100, x.sqrt(1)) + + x = BigDecimal('1' * 60 + '.' + '1' * 40) + assert_in_delta(BigDecimal('3' * 30 + '.' + '3' * 70), x.sqrt(1), BigDecimal('1e-70')) + + x = BigDecimal('1' * 40 + '.' + '1' * 60) + assert_in_delta(BigDecimal('3' * 20 + '.' + '3' * 80), x.sqrt(1), BigDecimal('1e-80')) + + x = BigDecimal('0.' + '0' * 50 + '1' * 100) + assert_in_delta(BigDecimal('0.' + '0' * 25 + '3' * 100), x.sqrt(1), BigDecimal('1e-125')) + end + def test_fix x = BigDecimal("1.1") assert_equal(1, x.fix) @@ -2431,17 +2455,19 @@ def test_BigMath_log_under_gc_stress EOS end - def test_exp_log_pow_with_limit + def test_sqrt_exp_log_pow_with_limit prec = 100 limit = 10 x = BigDecimal(123).div(7, prec) y = BigDecimal(456).div(11, prec) + sqrt = BigMath.sqrt(x, prec) exp = BigMath.exp(x, prec) log = BigMath.log(x, prec) pow = x.power(y, prec) pow_lim = x.power(y, limit) BigDecimal.save_limit do BigDecimal.limit(limit) + assert_equal(sqrt, BigMath.sqrt(x, prec)) assert_equal(exp, BigMath.exp(x, prec)) assert_equal(log, BigMath.log(x, prec)) assert_equal(pow, x.power(y, prec)) From d2bd549b6bba1e447188055b693e163a66e2b1e6 Mon Sep 17 00:00:00 2001 From: tompng Date: Sat, 19 Jul 2025 17:45:25 +0900 Subject: [PATCH 2/4] Use common prec validation also in BigDecimal#sqrt --- lib/bigdecimal.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/bigdecimal.rb b/lib/bigdecimal.rb index 4de8192e..5395cc9f 100644 --- a/lib/bigdecimal.rb +++ b/lib/bigdecimal.rb @@ -21,9 +21,13 @@ def self.coerce_to_bigdecimal(x, prec, method_name) # :nodoc: raise ArgumentError, "#{x.inspect} can't be coerced into BigDecimal" end - def self.validate_prec(prec, method_name) # :nodoc: + def self.validate_prec(prec, method_name, accept_zero: false) # :nodoc: raise ArgumentError, 'precision must be an Integer' unless Integer === prec - raise ArgumentError, "Zero or negative precision for #{method_name}" if prec <= 0 + if accept_zero + raise ArgumentError, "Negative precision for #{method_name}" if prec < 0 + else + raise ArgumentError, "Zero or negative precision for #{method_name}" if prec <= 0 + end end def self.infinity_computation_result # :nodoc: @@ -178,12 +182,12 @@ def power(y, prec = nil) # Result has at least prec significant digits. # def sqrt(prec) + Internal.validate_prec(prec, :sqrt, accept_zero: true) if infinite? == 1 exception_mode = BigDecimal.mode(BigDecimal::EXCEPTION_ALL) raise FloatDomainError, "Computation results in 'Infinity'" if exception_mode.anybits?(BigDecimal::EXCEPTION_INFINITY) return INFINITY end - raise ArgumentError, 'negative precision' if prec < 0 raise FloatDomainError, 'sqrt of negative value' if self < 0 raise FloatDomainError, "sqrt of 'NaN'(Not a Number)" if nan? return self if zero? From 6a718b36a9cb901fbce3dbbb922fecf1bb35d7c7 Mon Sep 17 00:00:00 2001 From: tompng Date: Mon, 28 Jul 2025 21:54:51 +0900 Subject: [PATCH 3/4] Use common EXCEPTION_INFINITY check in sqrt --- lib/bigdecimal.rb | 7 ++----- test/bigdecimal/test_bigdecimal.rb | 8 +++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/lib/bigdecimal.rb b/lib/bigdecimal.rb index 5395cc9f..2859b7da 100644 --- a/lib/bigdecimal.rb +++ b/lib/bigdecimal.rb @@ -183,11 +183,8 @@ def power(y, prec = nil) # def sqrt(prec) Internal.validate_prec(prec, :sqrt, accept_zero: true) - if infinite? == 1 - exception_mode = BigDecimal.mode(BigDecimal::EXCEPTION_ALL) - raise FloatDomainError, "Computation results in 'Infinity'" if exception_mode.anybits?(BigDecimal::EXCEPTION_INFINITY) - return INFINITY - end + return Internal.infinity_computation_result if infinite? == 1 + raise FloatDomainError, 'sqrt of negative value' if self < 0 raise FloatDomainError, "sqrt of 'NaN'(Not a Number)" if nan? return self if zero? diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index 46c15a9c..6caaf72b 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -1443,15 +1443,13 @@ def test_sqrt_bigdecimal assert_in_delta(BigDecimal("4.0000000000000000000125"), BigDecimal("16.0000000000000000001").sqrt(100), BigDecimal("1e-40")) - BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false) - BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) - assert_raise_with_message(FloatDomainError, "sqrt of 'NaN'(Not a Number)") { BigDecimal("NaN").sqrt(1) } - assert_raise_with_message(FloatDomainError, "sqrt of negative value") { BigDecimal("-Infinity").sqrt(1) } + assert_raise_with_message(FloatDomainError, "sqrt of 'NaN'(Not a Number)") { BigDecimal::NAN.sqrt(1) } + assert_raise_with_message(FloatDomainError, "sqrt of negative value") { NEGATIVE_INFINITY.sqrt(1) } assert_equal(0, BigDecimal("0").sqrt(1)) assert_equal(0, BigDecimal("-0").sqrt(1)) assert_equal(1, BigDecimal("1").sqrt(1)) - assert_positive_infinite(BigDecimal("Infinity").sqrt(1)) + assert_positive_infinite_calculation { BigDecimal::INFINITY.sqrt(1) } # Out of float range assert_equal(BigDecimal('12e1024'), BigDecimal('144e2048').sqrt(10)) From dae780db76a61c83ae93be34e3cb0eedad2b3005 Mon Sep 17 00:00:00 2001 From: tompng Date: Mon, 25 Aug 2025 21:25:53 +0900 Subject: [PATCH 4/4] Limit sqrt result when prec limit is set --- lib/bigdecimal.rb | 3 +++ test/bigdecimal/test_bigdecimal.rb | 2 ++ 2 files changed, 5 insertions(+) diff --git a/lib/bigdecimal.rb b/lib/bigdecimal.rb index 2859b7da..e1accd47 100644 --- a/lib/bigdecimal.rb +++ b/lib/bigdecimal.rb @@ -189,6 +189,8 @@ def sqrt(prec) raise FloatDomainError, "sqrt of 'NaN'(Not a Number)" if nan? return self if zero? + limit = BigDecimal.limit.nonzero? if prec == 0 + # BigDecimal#sqrt calculates at least n_significant_digits precision. # This feature maybe problematic for some cases. n_digits = n_significant_digits @@ -202,6 +204,7 @@ def sqrt(prec) precs.reverse_each do |p| y = y.add(x.div(y, p), p).div(2, p) end + y = y.mult(1, limit) if limit y.mult(BigDecimal("1e#{ex}"), precs.first) end end diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index 6caaf72b..09f4e95f 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -2459,6 +2459,7 @@ def test_sqrt_exp_log_pow_with_limit x = BigDecimal(123).div(7, prec) y = BigDecimal(456).div(11, prec) sqrt = BigMath.sqrt(x, prec) + sqrt_lim = sqrt.mult(1, limit) exp = BigMath.exp(x, prec) log = BigMath.log(x, prec) pow = x.power(y, prec) @@ -2466,6 +2467,7 @@ def test_sqrt_exp_log_pow_with_limit BigDecimal.save_limit do BigDecimal.limit(limit) assert_equal(sqrt, BigMath.sqrt(x, prec)) + assert_equal(sqrt_lim, BigMath.sqrt(x, 0)) assert_equal(exp, BigMath.exp(x, prec)) assert_equal(log, BigMath.log(x, prec)) assert_equal(pow, x.power(y, prec))