* 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
This commit is contained in:
muraken 2010-02-28 10:08:22 +00:00
Родитель 58e8688f69
Коммит 5073155a17
4 изменённых файлов: 103 добавлений и 83 удалений

Просмотреть файл

@ -1,3 +1,19 @@
Sun Feb 28 18:58:00 2010 Kenta Murata <mrkn@mrkn.jp>
* 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@fsij.org>
* pack.c (pack_pack): use union to avoid pointer cast.

Просмотреть файл

@ -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

117
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));

Просмотреть файл

@ -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