[前][次][番号順一覧][スレッド一覧]

ruby-changes:14915

From: muraken <ko1@a...>
Date: Sun, 28 Feb 2010 19:09:20 +0900 (JST)
Subject: [ruby-changes:14915] Ruby:r26785 (trunk): * math.c (rb_eMathDomainError): new exception class for representing mathematical domain error instead of Errno::EDOM.

muraken	2010-02-28 19:08:22 +0900 (Sun, 28 Feb 2010)

  New Revision: 26785

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=26785

  Log:
    * math.c (rb_eMathDomainError): new exception class for representing mathematical domain error instead of Errno::EDOM.
    * math.c (domain_check, infinity_check): removed, no longer needed.
    * math.c (math_atan2, math_acos, math_asin, math_acosh, math_atanh, math_log, math_log2, math_log10, math_sqrt, math_gamma, math_lgamma): mathematical domain errors are checked and raised before calling libm's functions.
    * test/ruby/test_math.rb: updated for changes of maht.c.

  Modified files:
    trunk/ChangeLog
    trunk/include/ruby/ruby.h
    trunk/math.c
    trunk/test/ruby/test_math.rb

Index: math.c
===================================================================
--- math.c	(revision 26784)
+++ math.c	(revision 26785)
@@ -16,6 +16,7 @@
 #define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
 
 VALUE rb_mMath;
+VALUE rb_eMathDomainError;
 
 extern VALUE rb_to_float(VALUE val);
 #define Need_Float(x) do {if (TYPE(x) != T_FLOAT) {(x) = rb_to_float(x);}} while(0)
@@ -24,45 +25,9 @@
     Need_Float(y);\
 } while (0)
 
-static void
-domain_check(double x, double y, const char *msg)
-{
-    if (errno) {
-	if (isinf(y)) return;
-    }
-    else {
-	if (!isnan(y)) return;
-	else if (isnan(x)) return;
-	else {
-#if defined(EDOM)
-	    errno = EDOM;
-#else
-	    errno = ERANGE;
-#endif
-	}
-    }
-    rb_sys_fail(msg);
-}
+#define domain_error(msg) \
+    rb_raise(rb_eMathDomainError, "Numerical argument is out of domain - " #msg);
 
-static void
-infinity_check(VALUE arg, double res, const char *msg)
-{
-    while(1) {
-	if (errno) {
-	    rb_sys_fail(msg);
-	}
-	if (isinf(res) && !isinf(RFLOAT_VALUE(arg))) {
-#if defined(EDOM)
-	    errno = EDOM;
-#elif defined(ERANGE)
-	    errno = ERANGE;
-#endif
-	    continue;
-	}
-	break;
-    }
-}
-
 /*
  *  call-seq:
  *     Math.atan2(y, x)  => float
@@ -86,8 +51,13 @@
 static VALUE
 math_atan2(VALUE obj, VALUE y, VALUE x)
 {
+    double dx, dy;
     Need_Float2(y, x);
-    return DBL2NUM(atan2(RFLOAT_VALUE(y), RFLOAT_VALUE(x)));
+    dx = RFLOAT_VALUE(x);
+    dy = RFLOAT_VALUE(y);
+    if (dx == 0.0 && dy == 0.0) domain_error("atan2");
+    if (isinf(dx) && isinf(dy)) domain_error("atan2");
+    return DBL2NUM(atan2(dy, dx));
 }
 
 
@@ -151,10 +121,10 @@
     double d0, d;
 
     Need_Float(x);
-    errno = 0;
     d0 = RFLOAT_VALUE(x);
+    /* check for domain error */
+    if (d0 < -1.0 || 1.0 < d0) domain_error("acos");
     d = acos(d0);
-    domain_check(d0, d, "acos");
     return DBL2NUM(d);
 }
 
@@ -171,10 +141,10 @@
     double d0, d;
 
     Need_Float(x);
-    errno = 0;
     d0 = RFLOAT_VALUE(x);
+    /* check for domain error */
+    if (d0 < -1.0 || 1.0 < d0) domain_error("asin");
     d = asin(d0);
-    domain_check(d0, d, "asin");
     return DBL2NUM(d);
 }
 
@@ -274,10 +244,10 @@
     double d0, d;
 
     Need_Float(x);
-    errno = 0;
     d0 = RFLOAT_VALUE(x);
+    /* check for domain error */
+    if (d0 < 1.0) domain_error("acosh");
     d = acosh(d0);
-    domain_check(d0, d, "acosh");
     return DBL2NUM(d);
 }
 
@@ -308,15 +278,13 @@
     double d0, d;
 
     Need_Float(x);
-    errno = 0;
     d0 = RFLOAT_VALUE(x);
-    if (d0 == 1.0 || d0 == -1.0) {
-	errno = ERANGE;
-	rb_sys_fail("atanh");
-    }
+    /* check for domain error */
+    if (d0 <  -1.0 || +1.0 <  d0) domain_error("atanh");
+    /* check for pole error */
+    if (d0 == -1.0) return DBL2NUM(-INFINITY);
+    if (d0 == +1.0) return DBL2NUM(+INFINITY);
     d = atanh(d0);
-    domain_check(d0, d, "atanh");
-    infinity_check(x, d, "atanh");
     return DBL2NUM(d);
 }
 
@@ -372,15 +340,16 @@
 
     rb_scan_args(argc, argv, "11", &x, &base);
     Need_Float(x);
-    errno = 0;
     d0 = RFLOAT_VALUE(x);
+    /* check for domain error */
+    if (d0 < 0.0) domain_error("log");
+    /* check for pole error */
+    if (d0 == 0.0) return DBL2NUM(-INFINITY);
     d = log(d0);
     if (argc == 2) {
 	Need_Float(base);
 	d /= log(RFLOAT_VALUE(base));
     }
-    domain_check(d0, d, "log");
-    infinity_check(x, d, "log");
     return DBL2NUM(d);
 }
 
@@ -415,11 +384,12 @@
     double d0, d;
 
     Need_Float(x);
-    errno = 0;
     d0 = RFLOAT_VALUE(x);
+    /* check for domain error */
+    if (d0 < 0.0) domain_error("log2");
+    /* check for pole error */
+    if (d0 == 0.0) return DBL2NUM(-INFINITY);
     d = log2(d0);
-    domain_check(d0, d, "log2");
-    infinity_check(x, d, "log2");
     return DBL2NUM(d);
 }
 
@@ -441,11 +411,12 @@
     double d0, d;
 
     Need_Float(x);
-    errno = 0;
     d0 = RFLOAT_VALUE(x);
+    /* check for domain error */
+    if (d0 < 0.0) domain_error("log10");
+    /* check for pole error */
+    if (d0 == 0.0) return DBL2NUM(-INFINITY);
     d = log10(d0);
-    domain_check(d0, d, "log10");
-    infinity_check(x, d, "log10");
     return DBL2NUM(d);
 }
 
@@ -479,10 +450,11 @@
     double d0, d;
 
     Need_Float(x);
-    errno = 0;
     d0 = RFLOAT_VALUE(x);
+    /* check for domain error */
+    if (d0 < 0.0) domain_error("sqrt");
+    if (d0 == 0.0) return DBL2NUM(0.0);
     d = sqrt(d0);
-    domain_check(d0, d, "sqrt");
     return DBL2NUM(d);
 }
 
@@ -686,15 +658,17 @@
     double intpart, fracpart;
     Need_Float(x);
     d0 = RFLOAT_VALUE(x);
+    /* check for domain error */
+    if (isinf(d0) && signbit(d0)) domain_error("gamma");
     fracpart = modf(d0, &intpart);
-    if (fracpart == 0.0 &&
-        0 < intpart &&
-        intpart - 1 < (double)numberof(fact_table)) {
-        return DBL2NUM(fact_table[(int)intpart - 1]);
+    if (fracpart == 0.0) {
+	if (intpart < 0) domain_error("gamma");
+	if (0 < intpart &&
+	    intpart - 1 < (double)numberof(fact_table)) {
+	    return DBL2NUM(fact_table[(int)intpart - 1]);
+	}
     }
-    errno = 0;
     d = tgamma(d0);
-    domain_check(d0, d, "gamma");
     return DBL2NUM(d);
 }
 
@@ -717,13 +691,13 @@
     int sign=1;
     VALUE v;
     Need_Float(x);
-    errno = 0;
     d0 = RFLOAT_VALUE(x);
+    /* check for domain error */
     if (isinf(d0)) {
+	if (signbit(d0)) domain_error("lgamma");
 	return rb_assoc_new(DBL2NUM(INFINITY), INT2FIX(1));
     }
     d = lgamma_r(d0, &sign);
-    domain_check(d0, d, "lgamma");
     v = DBL2NUM(d);
     return rb_assoc_new(v, INT2FIX(sign));
 }
@@ -772,6 +746,7 @@
 Init_Math(void)
 {
     rb_mMath = rb_define_module("Math");
+    rb_eMathDomainError = rb_define_class_under(rb_mMath, "DomainError", rb_eArgError);
 
 #ifdef M_PI
     rb_define_const(rb_mMath, "PI", DBL2NUM(M_PI));
Index: include/ruby/ruby.h
===================================================================
--- include/ruby/ruby.h	(revision 26784)
+++ include/ruby/ruby.h	(revision 26785)
@@ -1234,6 +1234,8 @@
 RUBY_EXTERN VALUE rb_eSyntaxError;
 RUBY_EXTERN VALUE rb_eLoadError;
 
+RUBY_EXTERN VALUE rb_eMathDomainError;
+
 RUBY_EXTERN VALUE rb_stdin, rb_stdout, rb_stderr;
 
 static inline VALUE
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 26784)
+++ ChangeLog	(revision 26785)
@@ -1,3 +1,19 @@
+Sun Feb 28 18:58:00 2010  Kenta Murata  <mrkn@m...>
+
+	* math.c (rb_eMathDomainError): new exception class
+	  for representing mathematical domain error instead
+	  of Errno::EDOM.
+
+	* math.c (domain_check, infinity_check): removed,
+	  no longer needed.
+
+	* math.c (math_atan2, math_acos, math_asin, math_acosh,
+	  math_atanh, math_log, math_log2, math_log10, math_sqrt,
+	  math_gamma, math_lgamma): mathematical domain errors
+	  are checked and raised before calling libm's functions.
+
+	* test/ruby/test_math.rb: updated for changes of maht.c.
+
 Sun Feb 28 15:07:28 2010  Tanaka Akira  <akr@f...>
 
 	* pack.c (pack_pack): use union to avoid pointer cast.
Index: test/ruby/test_math.rb
===================================================================
--- test/ruby/test_math.rb	(revision 26784)
+++ test/ruby/test_math.rb	(revision 26785)
@@ -2,16 +2,26 @@
 
 class TestMath < Test::Unit::TestCase
   def assert_infinity(a, *rest)
-    rest = ["not infinity"] if rest.empty?
+    rest = ["not infinity: #{a.inspect}"] if rest.empty?
     assert(!a.finite?, *rest)
   end
 
+  def assert_nan(a, *rest)
+    rest = ["not nan: #{a.inspect}"] if rest.empty?
+    assert(a.nan?, *rest)
+  end
+
   def check(a, b)
     err = [Float::EPSILON * 4, [a.abs, b.abs].max * Float::EPSILON * 256].max
     assert_in_delta(a, b, err)
   end
 
   def test_atan2
+    assert_raise(Math::DomainError) { Math.atan2(0, 0) }
+    assert_raise(Math::DomainError) { Math.atan2(Float::INFINITY, Float::INFINITY) }
+    assert_raise(Math::DomainError) { Math.atan2(Float::INFINITY, -Float::INFINITY) }
+    assert_raise(Math::DomainError) { Math.atan2(-Float::INFINITY, Float::INFINITY) }
+    assert_raise(Math::DomainError) { Math.atan2(-Float::INFINITY, -Float::INFINITY) }
     check(0, Math.atan2(0, 1))
     check(Math::PI / 4, Math.atan2(1, 1))
     check(Math::PI / 2, Math.atan2(1, 0))
@@ -46,7 +56,9 @@
     check(1 * Math::PI / 4, Math.acos( 1.0 / Math.sqrt(2)))
     check(2 * Math::PI / 4, Math.acos( 0.0))
     check(4 * Math::PI / 4, Math.acos(-1.0))
-    assert_raise(Errno::EDOM, Errno::ERANGE) { Math.acos(2.0) }
+    assert_raise(Math::DomainError) { Math.acos(+1.0 + Float::EPSILON) }
+    assert_raise(Math::DomainError) { Math.acos(-1.0 - Float::EPSILON) }
+    assert_raise(Math::DomainError) { Math.acos(2.0) }
   end
 
   def test_asin
@@ -54,7 +66,9 @@
     check( 1 * Math::PI / 4, Math.asin( 1.0 / Math.sqrt(2)))
     check( 2 * Math::PI / 4, Math.asin( 1.0))
     check(-2 * Math::PI / 4, Math.asin(-1.0))
-    assert_raise(Errno::EDOM, Errno::ERANGE) { Math.asin(2.0) }
+    assert_raise(Math::DomainError) { Math.asin(+1.0 + Float::EPSILON) }
+    assert_raise(Math::DomainError) { Math.asin(-1.0 - Float::EPSILON) }
+    assert_raise(Math::DomainError) { Math.asin(2.0) }
   end
 
   def test_atan
@@ -86,7 +100,8 @@
     check(0, Math.acosh(1))
     check(1, Math.acosh((Math::E ** 1 + Math::E ** -1) / 2))
     check(2, Math.acosh((Math::E ** 2 + Math::E ** -2) / 2))
-    assert_raise(Errno::EDOM, Errno::ERANGE) { Math.acosh(0) }
+    assert_raise(Math::DomainError) { Math.acosh(1.0 - Float::EPSILON) }
+    assert_raise(Math::DomainError) { Math.acosh(0) }
   end
 
   def test_asinh
@@ -99,7 +114,10 @@
     check(0, Math.atanh(Math.sinh(0) / Math.cosh(0)))
     check(1, Math.atanh(Math.sinh(1) / Math.cosh(1)))
     check(2, Math.atanh(Math.sinh(2) / Math.cosh(2)))
-    assert_raise(Errno::EDOM, Errno::ERANGE) { Math.atanh(-1) }
+    assert_nothing_raised { assert_infinity Math.atanh(1) }
+    assert_nothing_raised { assert_infinity -Math.atanh(-1) }
+    assert_raise(Math::DomainError) { Math.atanh(+1.0 + Float::EPSILON) }
+    assert_raise(Math::DomainError) { Math.atanh(-1.0 - Float::EPSILON) }
   end
 
   def test_exp
@@ -116,8 +134,9 @@
     check(1, Math.log(10, 10))
     check(2, Math.log(100, 10))
     assert_equal(1.0/0, Math.log(1.0/0))
-    assert_raise(Errno::EDOM, Errno::ERANGE) { Math.log(0) }
-    assert_raise(Errno::EDOM, Errno::ERANGE) { Math.log(-1) }
+    assert_nothing_raised { assert_infinity -Math.log(+0.0) }
+    assert_nothing_raised { assert_infinity -Math.log(-0.0) }
+    assert_raise(Math::DomainError) { Math.log(-1.0) }
     assert_raise(TypeError) { Math.log(1,nil) }
   end
 
@@ -126,8 +145,9 @@
     check(1, Math.log2(2))
     check(2, Math.log2(4))
     assert_equal(1.0/0, Math.log2(1.0/0))
-    assert_raise(Errno::EDOM, Errno::ERANGE) { Math.log2(0) }
-    assert_raise(Errno::EDOM, Errno::ERANGE) { Math.log2(-1) }
+    assert_nothing_raised { assert_infinity -Math.log2(+0.0) }
+    assert_nothing_raised { assert_infinity -Math.log2(-0.0) }
+    assert_raise(Math::DomainError) { Math.log2(-1.0) }
   end
 
   def test_log10
@@ -135,8 +155,9 @@
     check(1, Math.log10(10))
     check(2, Math.log10(100))
     assert_equal(1.0/0, Math.log10(1.0/0))
-    assert_raise(Errno::EDOM, Errno::ERANGE) { Math.log10(0) }
-    assert_raise(Errno::EDOM, Errno::ERANGE) { Math.log10(-1) }
+    assert_nothing_raised { assert_infinity -Math.log10(+0.0) }
+    assert_nothing_raised { assert_infinity -Math.log10(-0.0) }
+    assert_raise(Math::DomainError) { Math.log10(-1.0) }
   end
 
   def test_sqrt
@@ -144,7 +165,8 @@
     check(1, Math.sqrt(1))
     check(2, Math.sqrt(4))
     assert_equal(1.0/0, Math.sqrt(1.0/0))
-    assert_raise(Errno::EDOM, Errno::ERANGE) { Math.sqrt(-1) }
+    assert_equal("0.0", Math.sqrt(-0.0).to_s) # insure it is +0.0, not -0.0
+    assert_raise(Math::DomainError) { Math.sqrt(-1.0) }
   end
 
   def test_frexp
@@ -201,6 +223,8 @@
       assert_infinity(Math.gamma(i), "Math.gamma(#{i}) should be INF")
       assert_infinity(Math.gamma(i-1), "Math.gamma(#{i-1}) should be INF")
     end
+
+    assert_raise(Math::DomainError) { Math.gamma(-Float::INFINITY) }
   end
 
   def test_lgamma
@@ -241,6 +265,9 @@
     g, s = Math.lgamma(4)
     check(Math.log(6), g)
     assert_equal(s, 1)
+
+    assert_infinity Math.lgamma(-Float::INFINITY)
+    assert_raise(Math::DomainError) { Math.lgamma(-Float::INFINITY) }
   end
 
   def test_cbrt

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]