From 5073155a178a9f478950afef4f148e44fd14b5d6 Mon Sep 17 00:00:00 2001 From: muraken Date: Sun, 28 Feb 2010 10:08:22 +0000 Subject: [PATCH] * 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@26785 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 16 ++++++ include/ruby/ruby.h | 2 + math.c | 117 ++++++++++++++++------------------------- test/ruby/test_math.rb | 51 +++++++++++++----- 4 files changed, 103 insertions(+), 83 deletions(-) diff --git a/ChangeLog b/ChangeLog index eafd5d2b6b..8fc1cc7fa2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +Sun Feb 28 18:58:00 2010 Kenta Murata + + * 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 * pack.c (pack_pack): use union to avoid pointer cast. diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index 5d87c0dbba..d56d42c86e 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -1234,6 +1234,8 @@ RUBY_EXTERN VALUE rb_eNameError; 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 diff --git a/math.c b/math.c index 51a89c3f55..d4251df28f 100644 --- a/math.c +++ b/math.c @@ -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,44 +25,8 @@ extern VALUE rb_to_float(VALUE val); 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); -} - -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; - } -} +#define domain_error(msg) \ + rb_raise(rb_eMathDomainError, "Numerical argument is out of domain - " #msg); /* * call-seq: @@ -86,8 +51,13 @@ infinity_check(VALUE arg, double res, const char *msg) 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 @@ math_acos(VALUE obj, VALUE x) 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 @@ math_asin(VALUE obj, VALUE x) 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 @@ math_acosh(VALUE obj, VALUE x) 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 @@ math_atanh(VALUE obj, VALUE x) 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 @@ math_log(int argc, VALUE *argv) 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 @@ math_log2(VALUE obj, VALUE x) 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 @@ math_log10(VALUE obj, VALUE x) 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 @@ math_sqrt(VALUE obj, VALUE x) 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 @@ math_gamma(VALUE obj, VALUE x) 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 @@ math_lgamma(VALUE obj, VALUE x) 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 @@ void 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)); diff --git a/test/ruby/test_math.rb b/test/ruby/test_math.rb index 5b49291df0..f7055d7b5b 100644 --- a/test/ruby/test_math.rb +++ b/test/ruby/test_math.rb @@ -2,16 +2,26 @@ require 'test/unit' 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 @@ class TestMath < Test::Unit::TestCase 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 @@ class TestMath < Test::Unit::TestCase 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 @@ class TestMath < Test::Unit::TestCase 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 @@ class TestMath < Test::Unit::TestCase 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 @@ class TestMath < Test::Unit::TestCase 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 @@ class TestMath < Test::Unit::TestCase 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 @@ class TestMath < Test::Unit::TestCase 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 @@ class TestMath < Test::Unit::TestCase 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 @@ class TestMath < Test::Unit::TestCase 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 @@ class TestMath < Test::Unit::TestCase 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