ruby-changes:33901
From: akr <ko1@a...>
Date: Sun, 18 May 2014 09:37:16 +0900 (JST)
Subject: [ruby-changes:33901] akr:r45982 (trunk): * configure.in: Check nextafter() availability.
akr 2014-05-18 09:37:10 +0900 (Sun, 18 May 2014) New Revision: 45982 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=45982 Log: * configure.in: Check nextafter() availability. * include/ruby/missing.h (nextafter): New optional declaration. * missing/nextafter.c: New file. * numeric.c: Float#next_float and Float#prev_float implemented. [ruby-core:62562] [Feature #9834] Added directories: trunk/ext/-test-/float/ trunk/test/-ext-/float/ Added files: trunk/ext/-test-/float/depend trunk/ext/-test-/float/extconf.rb trunk/ext/-test-/float/init.c trunk/ext/-test-/float/nextafter.c trunk/missing/nextafter.c trunk/test/-ext-/float/test_nextafter.rb Modified files: trunk/ChangeLog trunk/NEWS trunk/configure.in trunk/include/ruby/missing.h trunk/numeric.c trunk/test/ruby/test_float.rb Index: include/ruby/missing.h =================================================================== --- include/ruby/missing.h (revision 45981) +++ include/ruby/missing.h (revision 45982) @@ -168,6 +168,10 @@ RUBY_EXTERN int isnan(double); https://github.com/ruby/ruby/blob/trunk/include/ruby/missing.h#L168 # endif #endif +#ifndef HAVE_NEXTAFTER +RUBY_EXTERN double nextafter(double x, double y); +#endif + /* #ifndef HAVE_MEMCMP RUBY_EXTERN int memcmp(const void *, const void *, size_t); Index: configure.in =================================================================== --- configure.in (revision 45981) +++ configure.in (revision 45982) @@ -1934,6 +1934,7 @@ AC_REPLACE_FUNCS(isinf) https://github.com/ruby/ruby/blob/trunk/configure.in#L1934 AC_REPLACE_FUNCS(isnan) AC_REPLACE_FUNCS(lgamma_r) AC_REPLACE_FUNCS(memmove) +AC_REPLACE_FUNCS(nextafter) AC_REPLACE_FUNCS(setproctitle) AC_REPLACE_FUNCS(strchr) AC_REPLACE_FUNCS(strerror) Index: ChangeLog =================================================================== --- ChangeLog (revision 45981) +++ ChangeLog (revision 45982) @@ -1,3 +1,15 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Sun May 18 09:16:33 2014 Tanaka Akira <akr@f...> + + * configure.in: Check nextafter() availability. + + * include/ruby/missing.h (nextafter): New optional declaration. + + * missing/nextafter.c: New file. + + * numeric.c: Float#next_float and Float#prev_float implemented. + + [ruby-core:62562] [Feature #9834] + Sun May 18 09:02:17 2014 Tanaka Akira <akr@f...> * enum.c: Enumerable#slice_after implemented. Index: ext/-test-/float/depend =================================================================== --- ext/-test-/float/depend (revision 0) +++ ext/-test-/float/depend (revision 45982) @@ -0,0 +1,3 @@ https://github.com/ruby/ruby/blob/trunk/ext/-test-/float/depend#L1 +$(OBJS): $(HDRS) $(ruby_headers) + +nextafter.o: nextafter.c $(top_srcdir)/missing/nextafter.c Property changes on: ext/-test-/float/depend ___________________________________________________________________ Added: svn:eol-style + LF Index: ext/-test-/float/nextafter.c =================================================================== --- ext/-test-/float/nextafter.c (revision 0) +++ ext/-test-/float/nextafter.c (revision 45982) @@ -0,0 +1,36 @@ https://github.com/ruby/ruby/blob/trunk/ext/-test-/float/nextafter.c#L1 +#include "ruby.h" + +static VALUE +system_nextafter_m(VALUE klass, VALUE vx, VALUE vy) +{ + double x, y, z; + + x = NUM2DBL(vx); + y = NUM2DBL(vy); + z = nextafter(x, y); + + return DBL2NUM(z); +} + +#define nextafter missing_nextafter +#include "../../../missing/nextafter.c" +#undef nextafter + +static VALUE +missing_nextafter_m(VALUE klass, VALUE vx, VALUE vy) +{ + double x, y, z; + + x = NUM2DBL(vx); + y = NUM2DBL(vy); + z = missing_nextafter(x, y); + + return DBL2NUM(z); +} + +void +Init_nextafter(VALUE klass) +{ + rb_define_singleton_method(klass, "system_nextafter", system_nextafter_m, 2); + rb_define_singleton_method(klass, "missing_nextafter", missing_nextafter_m, 2); +} Property changes on: ext/-test-/float/nextafter.c ___________________________________________________________________ Added: svn:eol-style + LF Index: ext/-test-/float/init.c =================================================================== --- ext/-test-/float/init.c (revision 0) +++ ext/-test-/float/init.c (revision 45982) @@ -0,0 +1,11 @@ https://github.com/ruby/ruby/blob/trunk/ext/-test-/float/init.c#L1 +#include "ruby.h" + +#define init(n) {void Init_##n(VALUE klass); Init_##n(klass);} + +void +Init_float(void) +{ + VALUE mBug = rb_define_module("Bug"); + VALUE klass = rb_define_class_under(mBug, "Float", rb_cObject); + TEST_INIT_FUNCS(init); +} Property changes on: ext/-test-/float/init.c ___________________________________________________________________ Added: svn:eol-style + LF Index: ext/-test-/float/extconf.rb =================================================================== --- ext/-test-/float/extconf.rb (revision 0) +++ ext/-test-/float/extconf.rb (revision 45982) @@ -0,0 +1,7 @@ https://github.com/ruby/ruby/blob/trunk/ext/-test-/float/extconf.rb#L1 +$INCFLAGS << " -I$(topdir) -I$(top_srcdir)" +$srcs = Dir[File.join($srcdir, "*.{#{SRC_EXT.join(%q{,})}}")] +inits = $srcs.map {|s| File.basename(s, ".*")} +inits.delete("init") +inits.map! {|s|"X(#{s})"} +$defs << "-DTEST_INIT_FUNCS(X)=\"#{inits.join(' ')}\"" +create_makefile("-test-/float") Property changes on: ext/-test-/float/extconf.rb ___________________________________________________________________ Added: svn:eol-style + LF Property changes on: ext/-test-/float ___________________________________________________________________ Added: svn:ignore + Makefile extconf.h mkmf.log Index: NEWS =================================================================== --- NEWS (revision 45981) +++ NEWS (revision 45982) @@ -25,6 +25,11 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L25 * File * new class File::Statfs to hold filesystem information. (experimental) +* Float + * New methods: + * Float#next_float + * Float#prev_float + * IO * New methods * IO#statfs returns filesystem information as File::Statfs. (experimental) Index: numeric.c =================================================================== --- numeric.c (revision 45981) +++ numeric.c (revision 45982) @@ -1480,6 +1480,119 @@ flo_is_finite_p(VALUE num) https://github.com/ruby/ruby/blob/trunk/numeric.c#L1480 /* * call-seq: + * float.next_float -> float + * + * Returns the next representable floating-point number. + * + * Float::MAX.next_float and Float::INFINITY.next_float is Float::INFINITY. + * + * Float::NAN.next_float is Float::NAN. + * + * For example: + * + * p 0.01.next_float #=> 0.010000000000000002 + * p 1.0.next_float #=> 1.0000000000000002 + * p 100.0.next_float #=> 100.00000000000001 + * + * p 0.01.next_float - 0.01 #=> 1.734723475976807e-18 + * p 1.0.next_float - 1.0 #=> 2.220446049250313e-16 + * p 100.0.next_float - 100.0 #=> 1.4210854715202004e-14 + * + * f = 0.01; 20.times { printf "%-20a %s\n", f, f.to_s; f = f.next_float } + * #=> 0x1.47ae147ae147bp-7 0.01 + * # 0x1.47ae147ae147cp-7 0.010000000000000002 + * # 0x1.47ae147ae147dp-7 0.010000000000000004 + * # 0x1.47ae147ae147ep-7 0.010000000000000005 + * # 0x1.47ae147ae147fp-7 0.010000000000000007 + * # 0x1.47ae147ae148p-7 0.010000000000000009 + * # 0x1.47ae147ae1481p-7 0.01000000000000001 + * # 0x1.47ae147ae1482p-7 0.010000000000000012 + * # 0x1.47ae147ae1483p-7 0.010000000000000014 + * # 0x1.47ae147ae1484p-7 0.010000000000000016 + * # 0x1.47ae147ae1485p-7 0.010000000000000018 + * # 0x1.47ae147ae1486p-7 0.01000000000000002 + * # 0x1.47ae147ae1487p-7 0.010000000000000021 + * # 0x1.47ae147ae1488p-7 0.010000000000000023 + * # 0x1.47ae147ae1489p-7 0.010000000000000024 + * # 0x1.47ae147ae148ap-7 0.010000000000000026 + * # 0x1.47ae147ae148bp-7 0.010000000000000028 + * # 0x1.47ae147ae148cp-7 0.01000000000000003 + * # 0x1.47ae147ae148dp-7 0.010000000000000031 + * # 0x1.47ae147ae148ep-7 0.010000000000000033 + * + * f = 0.0 + * 100.times { f += 0.1 } + * p f #=> 9.99999999999998 # should be 10.0 in the ideal world. + * p 10-f #=> 1.9539925233402755e-14 # the floating-point error. + * p(10.0.next_float-10) #=> 1.7763568394002505e-15 # 1 ulp (units in the last place). + * p((10-f)/(10.0.next_float-10)) #=> 11.0 # the error is 11 ulp. + * p((10-f)/(10*Float::EPSILON)) #=> 8.8 # approximation of the above. + * p "%a" % f #=> "0x1.3fffffffffff5p+3" # the last hex digit is 5. 16 - 5 = 11 ulp. + * + */ +static VALUE +flo_next_float(VALUE vx) +{ + double x, y; + x = NUM2DBL(vx); + y = nextafter(x, INFINITY); + return DBL2NUM(y); +} + +/* + * call-seq: + * float.prev_float -> float + * + * Returns the previous representable floatint-point number. + * + * (-Float::MAX).prev_float and (-Float::INFINITY).prev_float is -Float::INFINITY. + * + * Float::NAN.prev_float is Float::NAN. + * + * For example: + * + * p 0.01.prev_float #=> 0.009999999999999998 + * p 1.0.prev_float #=> 0.9999999999999999 + * p 100.0.prev_float #=> 99.99999999999999 + * + * p 0.01 - 0.01.prev_float #=> 1.734723475976807e-18 + * p 1.0 - 1.0.prev_float #=> 1.1102230246251565e-16 + * p 100.0 - 100.0.prev_float #=> 1.4210854715202004e-14 + * + * f = 0.01; 20.times { printf "%-20a %s\n", f, f.to_s; f = f.prev_float } + * #=> 0x1.47ae147ae147bp-7 0.01 + * # 0x1.47ae147ae147ap-7 0.009999999999999998 + * # 0x1.47ae147ae1479p-7 0.009999999999999997 + * # 0x1.47ae147ae1478p-7 0.009999999999999995 + * # 0x1.47ae147ae1477p-7 0.009999999999999993 + * # 0x1.47ae147ae1476p-7 0.009999999999999992 + * # 0x1.47ae147ae1475p-7 0.00999999999999999 + * # 0x1.47ae147ae1474p-7 0.009999999999999988 + * # 0x1.47ae147ae1473p-7 0.009999999999999986 + * # 0x1.47ae147ae1472p-7 0.009999999999999985 + * # 0x1.47ae147ae1471p-7 0.009999999999999983 + * # 0x1.47ae147ae147p-7 0.009999999999999981 + * # 0x1.47ae147ae146fp-7 0.00999999999999998 + * # 0x1.47ae147ae146ep-7 0.009999999999999978 + * # 0x1.47ae147ae146dp-7 0.009999999999999976 + * # 0x1.47ae147ae146cp-7 0.009999999999999974 + * # 0x1.47ae147ae146bp-7 0.009999999999999972 + * # 0x1.47ae147ae146ap-7 0.00999999999999997 + * # 0x1.47ae147ae1469p-7 0.009999999999999969 + * # 0x1.47ae147ae1468p-7 0.009999999999999967 + * + */ +static VALUE +flo_prev_float(VALUE vx) +{ + double x, y; + x = NUM2DBL(vx); + y = nextafter(x, -INFINITY); + return DBL2NUM(y); +} + +/* + * call-seq: * float.floor -> integer * * Returns the largest integer less than or equal to +float+. @@ -4106,6 +4219,8 @@ Init_Numeric(void) https://github.com/ruby/ruby/blob/trunk/numeric.c#L4219 rb_define_method(rb_cFloat, "nan?", flo_is_nan_p, 0); rb_define_method(rb_cFloat, "infinite?", flo_is_infinite_p, 0); rb_define_method(rb_cFloat, "finite?", flo_is_finite_p, 0); + rb_define_method(rb_cFloat, "next_float", flo_next_float, 0); + rb_define_method(rb_cFloat, "prev_float", flo_prev_float, 0); sym_to = ID2SYM(rb_intern("to")); sym_by = ID2SYM(rb_intern("by")); Index: test/ruby/test_float.rb =================================================================== --- test/ruby/test_float.rb (revision 45981) +++ test/ruby/test_float.rb (revision 45982) @@ -619,4 +619,39 @@ class TestFloat < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_float.rb#L619 assert_in_epsilon(10.0, ("1."+"1"*300000).to_f*9) end; end + + def test_next_float + smallest = 0.0.next_float + assert_equal(-Float::MAX, (-Float::INFINITY).next_float) + assert_operator(-Float::MAX, :<, (-Float::MAX).next_float) + assert_equal(Float::EPSILON/2, (-1.0).next_float + 1.0) + assert_operator(0.0, :<, smallest) + assert_operator([0.0, smallest], :include?, smallest/2) + assert_equal(Float::EPSILON, 1.0.next_float - 1.0) + assert_equal(Float::INFINITY, Float::MAX.next_float) + assert_equal(Float::INFINITY, Float::INFINITY.next_float) + assert(Float::NAN.next_float.nan?) + end + + def test_prev_float + smallest = 0.0.next_float + assert_equal(-Float::INFINITY, (-Float::INFINITY).prev_float) + assert_equal(-Float::INFINITY, (-Float::MAX).prev_float) + assert_equal(-Float::EPSILON, (-1.0).prev_float + 1.0) + assert_equal(-smallest, 0.0.prev_float) + assert_operator([0.0, 0.0.prev_float], :include?, 0.0.prev_float/2) + assert_equal(-Float::EPSILON/2, 1.0.prev_float - 1.0) + assert_operator(Float::MAX, :>, Float::MAX.prev_float) + assert_equal(Float::MAX, Float::INFINITY.prev_float) + assert(Float::NAN.prev_float.nan?) + end + + def test_next_prev_float_zero + z = 0.0.next_float.prev_float + assert_equal(0.0, z) + assert_equal(Float::INFINITY, 1.0/z) + z = 0.0.prev_float.next_float + assert_equal(0.0, z) + assert_equal(-Float::INFINITY, 1.0/z) + end end Index: test/-ext-/float/test_nextafter.rb =================================================================== --- test/-ext-/float/test_nextafter.rb (revision 0) +++ test/-ext-/float/test_nextafter.rb (revision 45982) @@ -0,0 +1,57 @@ https://github.com/ruby/ruby/blob/trunk/test/-ext-/float/test_nextafter.rb#L1 +require 'test/unit' +require "-test-/float" + +class TestFloatExt < Test::Unit::TestCase + NEXTAFTER_VALUES = [ + -Float::INFINITY, + -Float::MAX, + -100.0, + -1.0-Float::EPSILON, + -1.0, + -Float::EPSILON, + -Float::MIN/2, + -Math.ldexp(0.5, Float::MIN_EXP - Float::MANT_DIG + 1), + -0.0, + 0.0, + Math.ldexp(0.5, Float::MIN_EXP - Float::MANT_DIG + 1), + Float::MIN/2, + Float::MIN, + Float::EPSILON, + 1.0, + 1.0+Float::EPSILON, + 100.0, + Float::MAX, + Float::INFINITY, + Float::NAN + ] + + test_number = 0 + NEXTAFTER_VALUES.each {|n1| + NEXTAFTER_VALUES.each {|n2| + tag = n2.infinite? ? "ruby" : "other" + test_name = "test_nextafter_#{test_number}_#{tag}_#{n1}_#{n2}" + test_number += 1 + define_method(test_name) { + v1 = Bug::Float.missing_nextafter(n1, n2) + v2 = Bug::Float.system_nextafter(n1, n2) + assert_kind_of(Float, v1) + assert_kind_of(Float, v2) + if v1.nan? + assert(v2.nan?, "Bug::Float.system_nextafter(#{n1}, #{n2}).nan?") + else + assert_equal(v1, v2, + "Bug::Float.missing_nextafter(#{'%a' % n1}, #{'%a' % n2}) = #{'%a' % v1} != " + + "#{'%a' % v2} = Bug::Float.system_nextafter(#{'%a' % n1}, #{'%a' % n2})") + if v1 == 0 + s1 = 1.0/v1 < 0 ? "negative-zero" : "positive-zero" + s2 = 1.0/v2 < 0 ? "negative-zero" : "positive-zero" + assert_equal(s1, s2, + "Bug::Float.missing_nextafter(#{'%a' % n1}, #{'%a' % n2}) = #{'%a' % v1} != " + + "#{'%a' % v2} = Bug::Float.system_nextafter(#{'%a' % n1}, #{'%a' % n2})") + end + end + } + } + } + +end Property changes on: test/-ext-/float/test_nextafter.rb ___________________________________________________________________ Added: svn:eol-style + LF Index: missing/nextafter.c =================================================================== --- missing/nextafter.c (revision 0) +++ missing/nextafter.c (revision 45982) @@ -0,0 +1,75 @@ https://github.com/ruby/ruby/blob/trunk/missing/nextafter.c#L1 +#include <math.h> +#include <float.h> + +/* This function doesn't set errno. It should on POSIX, though. */ + +double +nextafter(double x, double y) +{ + double x1, x2, d; + int e; + + if (isnan(x)) + return x; + if (isnan(y)) + return y; + + if (x == y) + return y; + + if (x == 0) { + /* the minimum "subnormal" float */ + x1 = ldexp(0.5, DBL_MIN_EXP - DBL_MANT_DIG + 1); + if (x1 == 0) + x1 = DBL_MIN; /* the minimum "normal" float */ + if (0 < y) + return x1; + else + return -x1; + } + + if (x < 0) { + if (isinf(x)) + return -DBL_MAX; + if (x == -DBL_MAX && y < 0 && isinf(y)) + return y; + } + else { + if (isinf(x)) + return DBL_MAX; + if (x == DBL_MAX && 0 < y && isinf(y)) + return y; + } + + x1 = frexp(x, &e); + + if (x < y) { + d = DBL_EPSILON/2; + if (x1 == -0.5) { + x1 *= 2; + e--; + } + } + else { + d = -DBL_EPSILON/2; + if (x1 == 0.5) { + x1 *= 2; + e--; + } + } + + if (e < DBL_MIN_EXP) { + d = ldexp(d, DBL_MIN_EXP-e); + } + + x2 = x1 + d; + + if (x2 == 0.0) { + if (x1 < 0) + return -0.0; + else + return +0.0; + } + + return ldexp(x2, e); +} Property changes on: missing/nextafter.c ___________________________________________________________________ Added: svn:eol-style + LF -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/