From: "Masahiro Kanai (CanI)" Date: 2009-09-23T19:12:28+09:00 Subject: [ruby-dev:39367] Almost endless loop of BigMath::atan(x) when x.abs >= 1 金井 仁弘と申します。 以下のようにすると、計算量の増大により、 ほぼ無限ループに入ります。 $ ./ruby -v ruby 1.9.2dev (2009-09-23 trunk 25052) [i686-linux] $ ./ruby -e ' require "bigdecimal" require "bigdecimal/math" N = 20 p BigMath::atan(BigDecimal("1.0"), N) ' これは、BigMath::atan(x)のx.absが1以上の時に、発生する問題のようです。 これにより、make test-allも途中で無限ループに入ります。 x.abs >= 1のとき、 atan(x) = pi/2 - atan(1/x) を用いて、以下のように書き直しました。 また、上の式でも、xが1に非常に近い数値の場合は、 近似値を求めるために莫大な計算量が必要なため、 x.abs.round(prec) == 1のとき、 atan(x) = atan((-1+sqrt(1+x**2, prec))/x) としました。 よろしくお願いいたします。 --- /ext/bigdecimal/lib/bigdecimal/math.rb (revision 25052) +++ /ext/bigdecimal/lib/bigdecimal/math.rb (working copy) @@ -122,11 +122,18 @@ raise ArgumentError, "Zero or negative precision for atan" if prec <= 0 return BigDecimal("NaN") if x.infinite? || x.nan? n = prec + BigDecimal.double_fig - y = x + if 1 > x.abs + x1 = x + elsif 1 == x.abs.round(prec) + x1 = (-1 + sqrt(1 + x**2, prec))/x + else + x1 = 1 / x + end + y = x1 d = y - t = x + t = x1 r = BigDecimal("3") - x2 = x.mult(x,n) + x2 = x1.mult(x1,n) while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0) m = BigDecimal.double_fig if m < BigDecimal.double_fig t = -t.mult(x2,n) @@ -134,7 +141,13 @@ y += d r += 2 end - y + if 1 > x.abs + y + elsif 1 == x.abs.round(prec) + y*2 + else + PI(prec)*y.sign/4 - y + end end # Computes the value of e (the base of natural logarithms) raised to the -- Masahiro Kanai (CanI) http://d.hatena.ne.jp/CanI/