From: Yui NARUSE Date: 2010-03-16T04:01:52+09:00 Subject: [ruby-dev:40650] [Feature #2969] String#to_f が -h.hhh±pd を解釈できるように Feature #2969: String#to_f が -h.hhh±pd を解釈できるように http://redmine.ruby-lang.org/issues/show/2969 起票者: Yui NARUSE ステータス: Open, 優先度: Normal C99 の printf には a という指定子があります。 aA The argument is printed in style ‘[-h.hhh±pd]’ where there is one digit before the hexadecimal point and the number after is equal to the precision specification for the argument; when the precision is missing, enough digits are produced to convey the argument's exact double-precision floating-point representation. The values ∞ and NaN are printed as ‘inf’ and ‘nan’, respectively. これを使うと、以下のような形で整形されます。 -0.0 #=> "-0x0p+0" 729.0/10 #=> "0x1.239999999999ap+6" Math.log(3) #=> "0x1.193ea7aad030ap+0" Math.exp(100) #=> "0x1.3494a9b171bf5p+144" この形式の利点は、複雑な浮動小数点数を比較的少ない文字数で正確に表せることと、 仮数部が16進であるため丸めが起きていることを説明する際に便利な点が挙げられます。 で、この形式を使っているのですが、RubyのString#to_f で解釈してくれず悲しくなるので、 解釈できるようにしませんか。 パッチは以下の通りです。 diff --git a/util.c b/util.c index 5ebc5f3..e361d51 100644 --- a/util.c +++ b/util.c @@ -2106,6 +2106,44 @@ ruby_strtod(const char *s00, char **se) } break2: if (*s == '0') { + if (s[1] == 'x') { + static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF"; + s0 = ++s; + adj = 0; + + while (*++s && (s1 = strchr(hexdigit, *s))) { + adj *= 16; + adj += (s1 - hexdigit) & 15; + } + + if (*s == '.') { + aadj = 1.; + while (*++s && (s1 = strchr(hexdigit, *s))) { + aadj /= 16; + adj += aadj * ((s1 - hexdigit) & 15); + } + } + + if (*s != 'p') { + s = s0; + goto ret; + } + + dsign = 0x2C - *++s; /* +: 2B, -: 2D */ + if (abs(dsign) != 1) { + s = s0; + goto ret; + } + + for (nd = 0, s++; (c = *s) >= '0' && c <= '9'; s++) { + nd *= 10; + nd += c; + nd -= '0'; + } + + dval(rv) = ldexp(adj, nd * dsign); + goto ret; + } nz0 = 1; while (*++s == '0') ; if (!*s) diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 64205f6..72d3242 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -1381,10 +1381,24 @@ class TestString < Test::Unit::TestCase end def test_to_f + assert_equal(0.0, S("0.0").to_f) + assert_equal(?0, S("0.0").to_f.to_s[0]) + assert_equal(-0.0, S("-0.0").to_f) + assert_equal(?-, S("-0.0").to_f.to_s[0]) assert_equal(344.3, S("344.3").to_f) assert_equal(5.9742e24, S("5.9742e24").to_f) assert_equal(98.6, S("98.6 degrees").to_f) assert_equal(0.0, S("degrees 100.0").to_f) + assert_equal(0.0, S("0x0p+0").to_f) + assert_equal(?0, S("0x0p+0").to_f.to_s[0]) + assert_equal(-0.0, S("-0x0p+0").to_f) + assert_equal(?-, S("-0x0p+0").to_f.to_s[0]) + assert_equal(1.0, S("0x1p+0").to_f) + assert_equal(?1, S("0x1p+0").to_f.to_s[0]) + assert_equal(1024.0, S("0x1p+10").to_f) + assert_equal(0.0009765625, S("0x1p-10").to_f) + assert_equal(2.6881171418161356e+43, S("0x1.3494a9b171bf5p+144").to_f) + assert_equal(-3.720075976020836e-44, S("-0x1.a8c1f14e2af5dp-145").to_f) end def test_to_i ---------------------------------------- http://redmine.ruby-lang.org