```Hi,

At Fri, 1 May 2009 21:12:52 +0900,
Roger Pack wrote in [ruby-core:23345]:
> True--however the (current) code for String#to_s attempts to
> determine whether the floating point number "is the
> equivalent default for the rounded value" (i.e. if it round
> trips).

What about this?

Index: rational.c
===================================================================
--- rational.c	(revision 23433)
+++ rational.c	(working copy)
@@ -1286,4 +1286,5 @@ integer_to_r(VALUE self)
}

+#if 0
static void
float_decode_internal(VALUE self, VALUE *rf, VALUE *rn)
@@ -1299,5 +1300,4 @@ float_decode_internal(VALUE self, VALUE
}

-#if 0
static VALUE
float_decode(VALUE self)
@@ -1310,11 +1310,82 @@ float_decode(VALUE self)
#endif

+#if FLT_RADIX == 2 && SIZEOF_BDIGITS * 2 * CHAR_BIT > DBL_MANT_DIG
+# ifdef HAVE_LONG_LONG
+#   define BDIGITDBL2NUM(x) ULL2NUM(x)
+# else
+#   define BDIGITDBL2NUM(x) ULONG2NUM(x)
+# endif
+#else
+# define NEEDS_FDIV
+static ID id_fdiv;
+fun2(fdiv)
+#endif
+
+static VALUE
+float_r_round(double a, double f, int n)
+{
+    int i, r;
+#ifdef BDIGITDBL2NUM
+    BDIGIT_DBL fn = (BDIGIT_DBL)fabs(f);
+    BDIGIT_DBL d1 = (BDIGIT_DBL)1 << -n, d2 = d1;
+    BDIGIT_DBL rv = d1 % fn;
+    VALUE b, d;
+    if (rv < 10) {
+	for (i = 1, r = (int)rv; i <= r; ++i) {
+	    if ((double)fn / --d2 != a) break;
+	    if (fn % (d1 = d2) == 0) break;
+	}
+    }
+    else if ((rv = fn - rv) && rv < 10) {
+	for (i = 1, r = (int)rv; i <= r; ++i) {
+	    if ((double)fn / ++d2 != a) break;
+	    if (fn % (d1 = d2) == 0) break;
+	}
+    }
+    b = BDIGITDBL2NUM(fn);
+    d = BDIGITDBL2NUM(d1);
+    if (f < 0) b = f_negate(b);
+#else
+    VALUE d2, fn, rv;
+    VALUE b = rb_dbl2big(f);
+    VALUE d = rb_big_pow(rb_uint2big(FLT_RADIX), INT2FIX(-n));
+    if (FIXNUM_P(d)) {
+	d = rb_uint2big(FIX2LONG(d));
+    }
+    d2 = d;
+    fn = f_abs(b);
+    rv = rb_big_modulo(d, fn);
+    if (FIXNUM_P(rv) && (r = FIX2LONG(rv)) < 10) {
+	for (i = 1; i <= r; ++i) {
+	    d2 = f_sub(d2, INT2FIX(1));
+	    if (RFLOAT_VALUE(f_fdiv(fn, d2)) != a) break;
+	    if (f_mod(fn, d = d2) == INT2FIX(0)) break;
+	}
+    }
+    else if (FIXNUM_P(rv = f_sub(fn, rv)) && (r = FIX2LONG(rv)) < 10) {
+	for (i = 1; i <= r; ++i) {
+	    d2 = f_add(d2, INT2FIX(1));
+	    if (RFLOAT_VALUE(f_fdiv(fn, d2)) != a) break;
+	    if (f_mod(fn, d = d2) == INT2FIX(0)) break;
+	}
+    }
+#endif
+    return rb_rational_new(b, d);
+}
+
static VALUE
float_to_r(VALUE self)
{
-    VALUE f, n;
+    double a, f;
+    int n;

-    float_decode_internal(self, &f, &n);
-    return f_mul(f, f_expt(INT2FIX(FLT_RADIX), n));
+    a = RFLOAT_VALUE(self);
+    f = frexp(a, &n);
+    f = ldexp(f, DBL_MANT_DIG);
+    n -= DBL_MANT_DIG;
+    if (n <= DBL_MANT_DIG && f != 0) {
+	return float_r_round(a, f, n);
+    }
+    return f_mul(rb_dbl2big(f), f_expt(INT2FIX(FLT_RADIX), INT2FIX(n)));
}

@@ -1569,4 +1640,7 @@ Init_Rational(void)
id_to_s = rb_intern("to_s");
id_truncate = rb_intern("truncate");
+#ifdef NEEDS_FDIV
+    id_fdiv = rb_intern("fdiv");
+#endif

ml = (long)(log(DBL_MAX) / log(2.0) - 1);

--
Nobu Nakada

```