山本です。

util.c(ruby_strtod) にバグがありました _no

E:\ruby-cvs>ruby -ve "p Float('.')"
ruby 1.8.1 (2003-12-25) [i386-bccwin32]
-e:1:in `Float': invalid value for Float(): "." (ArgumentError)
        from -e:1

E:\ruby-cvs\ruby_1_8>miniruby -ve "p Float('.')"
ruby 1.8.2 (2004-07-09) [i386-bccwin32]
0.0

というように、数字が全くない場合にも 0.0 となっていました。

0.0000000000001 で preceding 0 を mantissa に数えないように変更したとき、
入ったバグです。

コメントでは "-I.FE-X" で Either I or F may be omitted, or both.
とあるのですが、ruby1.8.1, mingw32, bcc32 のいずれも両方省略すると
変換しないので、やはりバグだと思います。

下のテストと一緒に、明日の正午コミットしようと思います。問題があれば、
ご指摘お願いします。

Index: util.c
===================================================================
RCS file: /var/cvs/src/ruby/util.c,v
retrieving revision 1.43
diff -u -w -b -p -r1.43 util.c
--- util.c	14 May 2004 03:17:29 -0000	1.43
+++ util.c	13 Jul 2004 09:51:58 -0000
@@ -722,7 +722,8 @@ ruby_strtod(string, endPtr)
 				 * fractional part of the mantissa, and X
 				 * is the exponent.  Either of the signs
 				 * may be "+", "-", or omitted.  Either I
-				 * or F may be omitted, or both.  The decimal
+				 * or F may be omitted, but cannot be ommitted
+				 * at once. The decimal
 				 * point isn't necessary unless F is present.
 				 * The "E" may actually be an "e".  E and X
 				 * may both be omitted (but not just one).
@@ -745,7 +746,8 @@ ruby_strtod(string, endPtr)
 				 * case, fracExp is incremented one for each
 				 * dropped digit. */
     int mantSize = 0;		/* Number of digits in mantissa. */
-    int decPt = FALSE;		/* mantissa has decimal point. */
+    int hasPoint = FALSE;	/* Decimal point exists. */
+    int hasDigit = FALSE;	/* I or F exists. */
     const char *pMant;		/* Temporarily holds location of mantissa
 				 * in string. */
     const char *pExp;		/* Temporarily holds location of exponent
@@ -778,13 +780,13 @@ ruby_strtod(string, endPtr)
 
     for ( ; c = *p; p += 1) {
 	if (!ISDIGIT(c)) {
-	    if (c != '.' || decPt) {
+	    if (c != '.' || hasPoint) {
 		break;
 	    }
-	    decPt = TRUE;
+	    hasPoint = TRUE;
 	}
 	else {
-	    if (decPt) { /* already in fractional part */
+	    if (hasPoint) { /* already in fractional part */
 		fracExp -= 1;
 	    }
 	    if (mantSize) { /* already in mantissa */
@@ -794,6 +796,7 @@ ruby_strtod(string, endPtr)
 		mantSize += 1;
 		pMant = p;
 	    }
+	    hasDigit = TRUE;
 	}
     }
 
@@ -812,7 +815,11 @@ ruby_strtod(string, endPtr)
 	fracExp += (mantSize - 18);
 	mantSize = 18;
     }
-    {
+    if (!hasDigit) {
+	fraction = 0.0;
+	p = string;
+    }
+    else {
 	int frac1, frac2;
 	frac1 = 0;
 	for ( ; mantSize > 9; mantSize -= 1) {


Index: test_float.rb
===================================================================
RCS file: /var/cvs/src/ruby/test/ruby/test_float.rb,v
retrieving revision 1.10
diff -u -w -b -p -r1.10 test_float.rb
--- test_float.rb	15 May 2004 08:54:23 -0000	1.10
+++ test_float.rb	13 Jul 2004 09:57:21 -0000
@@ -61,6 +61,23 @@ class TestFloat < Test::Unit::TestCase
     assert(a.abs < Float::EPSILON)
     a = Float("-0.0")
     assert(a.abs < Float::EPSILON)
+    a = Float(".0")
+    assert(a.abs < Float::EPSILON)
+    a = Float("+.0")
+    assert(a.abs < Float::EPSILON)
+    a = Float("-.0")
+    assert(a.abs < Float::EPSILON)
+    a = Float("0.")
+    assert(a.abs < Float::EPSILON)
+    a = Float("+0.")
+    assert(a.abs < Float::EPSILON)
+    a = Float("-0.")
+    assert(a.abs < Float::EPSILON)
+    assert_raise(ArgumentError){Float(".")}
+    assert_raise(ArgumentError){Float("+")}
+    assert_raise(ArgumentError){Float("+.")}
+    assert_raise(ArgumentError){Float("-")}
+    assert_raise(ArgumentError){Float("-.")}
     a = Float("0." + "00" * Float::DIG + "1")
     assert(a != 0.0)
     a = Float("+0." + "00" * Float::DIG + "1")