From: Yui NARUSE Date: 2010-10-18T18:01:03+09:00 Subject: [ruby-dev:42420] [Ruby 1.9-Feature#3961][Open] printfと精度指定と負の値と Feature #3961: printfと精度指定と負の値と http://redmine.ruby-lang.org/issues/show/3961 起票者: Yui NARUSE ステータス: Open, 優先度: Normal カテゴリ: core, Target version: 1.9.3 printf が、フォーマットに精度指定しつつ負の値を与えた時の挙動が、 Perl と必然性無く異なっていて不便です。 % ./ruby -e'printf("%#.8x", -1)' 0x..ffffff そもそもこの挙動は C 言語由来で、C の場合例えば以下の通りになります。 % cat test.c #include int main(void) { printf("%#x\n", -1); return 0; } % cc t.c && ./a.out 0xffffffff また、おそらく直接参考にしたであろう Perl では以下の通りです、 % perl -e'printf("%#x",-1)' 0xffffffffffffffff % perl -e'printf("%#.30x",-1)' 0x00000000000000ffffffffffffffff つまり、Perl (の 64bit int 版) では、64bitで補数を取っています。 さて、Ruby の場合多倍長整数が組み込みなため、補数を取ると無限に続いてしまうから、 % ./ruby -e'printf("%#x",-1)' 0x..f と .. で略すのは理にかなっていると思います。 しかし、現在の Ruby は精度を指定した際にも..がついてしまうので、 Perl のような動きを実現させる事ができません。 % ./ruby -e'printf("%#.8x",-1)' 0x..ffffff で、この .. っていらないと思うんです。 例えば浮動小数点数の場合には以下のように .. とか付けずにぶった切る訳で、 無限大方向と無限小方向という違いはあれど、 「Perlとかと同じ挙動にできない」というデメリットの方が大きいのではないでしょうか。 % ./ruby -e'printf("%#.8f",10.0/3)' 3.33333333 わたしの場合、CRuby 側の inspect が printf("%x", negative_value) などとしている部分の動作を RubySpec で Ruby で書く時に頭を抱えてしまいました。 というわけでパッチは例えば以下の通りです。 diff --git a/sprintf.c b/sprintf.c index 21509ea..0e97955 100644 --- a/sprintf.c +++ b/sprintf.c @@ -844,7 +844,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) } else { s = nbuf; - if (v < 0) { + if (v < 0 && !(flags & FPREC0)) { dots = 1; } snprintf(fbuf, sizeof(fbuf), "%%l%c", *p == 'X' ? 'x' : *p); @@ -892,7 +892,8 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) tmp1 = tmp = rb_big2str0(val, base, RBIGNUM_SIGN(val)); s = RSTRING_PTR(tmp); if (*s == '-') { - dots = 1; + if (!(flags & FPREC0)) + dots = 1; if (base == 10) { rb_warning("negative number for %%u specifier"); } @@ -925,14 +926,11 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) } } if (prefix && !prefix[1]) { /* octal */ - if (dots) { - prefix = 0; - } - else if (len == 1 && *s == '0') { + if (len == 1 && *s == '0') { len = 0; if (flags & FPREC) prec--; } - else if ((flags & FPREC) && (prec > len)) { + else if ((flags & FPREC) && (prec > len) && v >= 0) { prefix = 0; } } diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb index 96a1b62..24b6a34 100644 --- a/test/ruby/test_sprintf.rb +++ b/test/ruby/test_sprintf.rb @@ -24,12 +24,12 @@ class TestSprintf < Test::Unit::TestCase assert_equal("0000", sprintf("%.4b", 0)) assert_equal("0001", sprintf("%.4b", 1)) assert_equal("0010", sprintf("%.4b", 2)) - assert_equal("..11", sprintf("%.4b", -1)) + assert_equal("1111", sprintf("%.4b", -1)) assert_equal(" 0000", sprintf("%6.4b", 0)) assert_equal(" 0001", sprintf("%6.4b", 1)) assert_equal(" 0010", sprintf("%6.4b", 2)) - assert_equal(" ..11", sprintf("%6.4b", -1)) + assert_equal(" 1111", sprintf("%6.4b", -1)) assert_equal(" 0", sprintf("%#4b", 0)) assert_equal(" 0b1", sprintf("%#4b", 1)) @@ -44,12 +44,12 @@ class TestSprintf < Test::Unit::TestCase assert_equal("0000", sprintf("%#.4b", 0)) assert_equal("0b0001", sprintf("%#.4b", 1)) assert_equal("0b0010", sprintf("%#.4b", 2)) - assert_equal("0b..11", sprintf("%#.4b", -1)) + assert_equal("0b1111", sprintf("%#.4b", -1)) assert_equal(" 0000", sprintf("%#6.4b", 0)) assert_equal("0b0001", sprintf("%#6.4b", 1)) assert_equal("0b0010", sprintf("%#6.4b", 2)) - assert_equal("0b..11", sprintf("%#6.4b", -1)) + assert_equal("0b1111", sprintf("%#6.4b", -1)) assert_equal("+0", sprintf("%+b", 0)) assert_equal("+1", sprintf("%+b", 1)) @@ -288,6 +288,8 @@ class TestSprintf < Test::Unit::TestCase b1 = (/\.\./ =~ s1) != nil b2 = (/\.\./ =~ s2) != nil assert(b1 == b2, "[ruby-dev:33224]") + + assert_equal("ffffffff", sprintf("%.8x", -1)) end def test_named diff --git a/test/ruby/test_sprintf_comb.rb b/test/ruby/test_sprintf_comb.rb index 261732b..3105127 100644 --- a/test/ruby/test_sprintf_comb.rb +++ b/test/ruby/test_sprintf_comb.rb @@ -190,7 +190,7 @@ class TestSprintfComb < Test::Unit::TestCase if digits.last != radix-1 digits << (radix-1) end - sign = '..' + sign = '..' unless precision else sign = '-' end @@ -222,8 +222,8 @@ class TestSprintfComb < Test::Unit::TestCase end end if type == 'o' && hs - if digits.empty? || digits.last != d - digits << d + if digits.empty? || digits.last != 0 + prefix = '0' end end ---------------------------------------- http://redmine.ruby-lang.org