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

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/

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