## Feature #2969

### String#to_f が -h.hhh±pd を解釈できるように

Status:
Closed
Priority:
Normal
Assignee:
-
Target version:
-

Description

=begin
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.exp(100) #=> "0x1.3494a9b171bf5p+144"

この形式の利点は、複雑な浮動小数点数を比較的少ない文字数で正確に表せることと、

で、この形式を使っているのですが、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;
• while (*++s && (s1 = strchr(hexdigit, *s))) {
• adj += (s1 - hexdigit) & 15;
• } +
• if (*s == '.') {
• while (*++s && (s1 = strchr(hexdigit, *s))) {
• }
• } +
• 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
=end

#### Updated by mrkn (Kenta Murata)over 10 years ago

=begin

On 2010/03/16, at 4:01, Yui NARUSE wrote:

C99 の printf には a という指定子があります。

(snip)

この形式の利点は、複雑な浮動小数点数を比較的少ない文字数で正確に表せることと、

で、この形式を使っているのですが、RubyのString#to_f で解釈してくれず悲しくなるので、

この機能、私も欲しいです。

--
Kenta Murata
OpenPGP FP = FA26 35D7 4F98 3498 0810 E0D5 F213 966F E9EB 0BCC

『Ruby 逆引きレシピ』 http://www.amazon.co.jp/dp/4798119881/mrkn-22

E-mail: mrkn@mrkn.jp
blog: http://d.hatena.ne.jp/mrkn/

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

• Status changed from Open to Closed
• % Done changed from 0 to 100

=begin
This issue was solved with changeset r26965.
Yui, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.

=end

=begin

るのでしょうか。

あと、これを浮動小数点数において16進数を受けつけるようにした、と解釈す
ると、たとえば、Integer では、

Integer('0xff') #=> 255
'0xff'.to_i #=> 0

という違いがあるようです。

Float('0xff.0p+0') #=> 255.0
'0xff.0p+0'.to_f #=> 255.0

それから、この形式がとても硬直しているように感じます。この形式は e を共
なう指数表記と似ていると思うのですが、1e1 と書けても、0x1p1 とは書けな
いし、0xff、0xff.0 なども受けつけないということですよね。

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin

るのでしょうか。

sprintf フォーマットに %a と %A を追加する提案を予定しています。
(つまり、String#% にも追加される)

ただ、現状 missing/vsnprintf.c が %a %A に対応しておらず、

1.9.3 ではこちらも入れたいと思っています。

あと、これを浮動小数点数において16進数を受けつけるようにした、と解釈す
ると、たとえば、Integer では、

Integer('0xff') #=> 255
'0xff'.to_i #=> 0

という違いがあるようです。

String#to_f は従来から指数表記を許していたので、
String#hex などと使い分けている String#to_i 事情が違うと理解しています。

Float('0xff.0p+0') #=> 255.0
'0xff.0p+0'.to_f #=> 255.0

それから、この形式がとても硬直しているように感じます。この形式は e を共
なう指数表記と似ていると思うのですが、1e1 と書けても、0x1p1 とは書けな
いし、0xff、0xff.0 なども受けつけないということですよね。

この提案では保守的に受け付ける範囲を狭めにしていました。
C99 の strtod と同等にするという案もあると思いますし、
わたしはそれに反対しません。

--
NARUSE, Yui naruse@airemix.jp

=end

=begin

String#to_f は従来から指数表記を許していたので、
String#hex などと使い分けている String#to_i 事情が違うと理解しています。

to_i では 0x をデファオルトでは数値として見ない、そして to_i はオプショ
ンを受け付けるということ。もしこれがあまり意味のないことだったら、to_f
というより to_i の仕様に疑問が生じます。hex は関係なく、'0xff'.to_i は
0 としかならないことに意味はないのか、ということです。意味のない使い分
けと断じていいですか。

この提案では保守的に受け付ける範囲を狭めにしていました。
C99 の strtod と同等にするという案もあると思いますし、
わたしはそれに反対しません。

それが保守的だとは思わないですが、現状のものだったら、むしろ採用して欲
しくないと思いました。

ら、一旦白紙に戻してやり直したほうがいいように思います。この中途半端提

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin

String#to_f は従来から指数表記を許していたので、
String#hex などと使い分けている String#to_i 事情が違うと理解しています。

to_i では 0x をデファオルトでは数値として見ない、そして to_i はオプショ
ンを受け付けるということ。もしこれがあまり意味のないことだったら、to_f
というより to_i の仕様に疑問が生じます。hex は関係なく、'0xff'.to_i は
0 としかならないことに意味はないのか、ということです。意味のない使い分
けと断じていいですか。

to_i がデフォルトで prefix を見ないのは、0377 のような、
0 詰めした数値文字列を、8 進数表記として解釈してしまうのを

パターンが無いので、やはり事情が異なると思います。

この提案では保守的に受け付ける範囲を狭めにしていました。
C99 の strtod と同等にするという案もあると思いますし、
わたしはそれに反対しません。

それが保守的だとは思わないですが、現状のものだったら、むしろ採用して欲
しくないと思いました。

ら、一旦白紙に戻してやり直したほうがいいように思います。この中途半端提

「保守的」である点についてはそこまでこだわりはないので、

diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb
index 9dd3ea9..c5b86fa 100644
--- a/test/ruby/test_string.rb
+++ b/test/ruby/test_string.rb
@@ -1398,7 +1398,10 @@ class TestString < Test::Unit::TestCase
assert_equal([-0.0].pack('G'), [S("-0.0").to_f].pack('G'))
assert_equal([ 0.0].pack('G'), [S(" 0x0p+0").to_f].pack('G'))
assert_equal([-0.0].pack('G'), [S("-0x0p+0").to_f].pack('G'))

• assert_equal(255.0, S("0Xff").to_f)
• assert_equal(255.5, S("0Xff.8").to_f) assert_equal(1.0, S("0X1.P+0").to_f)
• assert_equal(1024.0, S("0x1p10").to_f) 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) diff --git a/util.c b/util.c index 9cdb563..eebc979 100644 --- a/util.c +++ b/util.c @@ -2110,6 +2110,8 @@ break2: static const char hexdigit[] = "0123456789abcdef0123456789ABCDEF"; s0 = ++s; adj = 0;
• nd = 0;
• ```      dsign = 0;

while (*++s && (s1 = strchr(hexdigit, *s))) {
```

@@ -2124,21 +2126,18 @@ break2:
}
}

• ```      if (*s != 'P' && *s != 'p') {
```
• ```          s = s0;
```
• ```          goto ret;
```
• ```      }
```

-

• ```      dsign = 0x2C - *++s; /* +: 2B, -: 2D */
```
• ```      if (abs(dsign) != 1) {
```
• ```          s = s0;
```
• ```          goto ret;
```
• ```      }
```
• ```      if (*s == 'P' || *s == 'p') {
```
• ```          dsign = 0x2C - *++s; /* +: 2B, -: 2D */
```
• ```          if (abs(dsign) != 1) {
```
• ```              dsign = 1;
```
• ```              s--;
```
• ```          }
```
• ```      for (nd = 0, s++; (c = *s) >= '0' && c <= '9'; s++) {
```
• ```          nd *= 10;
```
• ```          nd += c;
```
• ```          nd -= '0';
```
• ```          while ((c = *++s) >= '0' && c <= '9') {
```
• ```              nd *= 10;
```
• ```              nd += c;
```
• ```              nd -= '0';
```
• ```          }
}

dval(rv) = ldexp(adj, nd * dsign);
```

--
NARUSE, Yui naruse@airemix.jp

=end

=begin

to_i がデフォルトで prefix を見ないのは、0377 のような、
0 詰めした数値文字列を、8 進数表記として解釈してしまうのを

パターンが無いので、やはり事情が異なると思います。

そうなのかもしれないけど、浮動小数点数にも16進数表記がある、という時点
でもう基本的条件は同じになったと考えたほうがいいと思います。

しかし、2進や8進も含むものとして打ち出せなかったところが提案がかなり微

るのはすっきりしません。

printf の対になるものは scanf なので、lib/scanf.rb で対応するくらいで十

printf の変更を考えてるなら、それを仕様と実装が用意出来てからでもいいわ
けだし、今すぐ採用する必要はないと思います。

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin

to_i がデフォルトで prefix を見ないのは、0377 のような、
0 詰めした数値文字列を、8 進数表記として解釈してしまうのを

パターンが無いので、やはり事情が異なると思います。

そうなのかもしれないけど、浮動小数点数にも16進数表記がある、という時点
でもう基本的条件は同じになったと考えたほうがいいと思います。

String#to_i は 0-prefix での 8 進数誤爆を防ぐために分けたのだと

しかし、2進や8進も含むものとして打ち出せなかったところが提案がかなり微

るのはすっきりしません。

よって、そこにない 2 進や 8 進を独自に追加する必要を感じません。

printf の対になるものは scanf なので、lib/scanf.rb で対応するくらいで十

そもそもが C99 のリテラルなので、printf にこだわることも無いと思います。

printf の変更を考えてるなら、それを仕様と実装が用意出来てからでもいいわ
けだし、今すぐ採用する必要はないと思います。

missing/vsnprintf.c 以外ならば、以下の通りです。
Windows 以外ならばこれで試すことはできるでしょう。

diff --git a/sprintf.c b/sprintf.c
index bec0569..e26b833 100644
--- a/sprintf.c
+++ b/sprintf.c
@@ -227,6 +227,10 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv)

• | equal to the precision, or in dd.dddd form otherwise.
• | The precision specifies the number of significant digits.
• G | Equivalent to `g', but use an uppercase`E' in exponent form.
• * a | Convert floating point argument as [-]0xh.hhhhp[+-]dd,
• * | which is consisted from optional sign, "0x", fraction part
• * | as hexadecimal, "p", and exponential part as decimal.
• * A | Equivalent to `a', but use uppercase`X' and `P'. *
• Field | Other Format
• ------+-------------------------------------------------------------- @@ -244,7 +248,7 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv)
• Flag | Applies to | Meaning
• ---------+---------------+-----------------------------------------
• space | bBdiouxX | Leave a space at the start of
• * | eEfgG | non-negative numbers.
• * | aAeEfgG | non-negative numbers.
• | (numeric fmt) | For `o',`x', `X',`b' and `B', use
• | | a minus sign with absolute value for
• | | negative values. @@ -255,19 +259,19 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv)
• | | sprintf string.
• ---------+---------------+-----------------------------------------
• # | bBoxX | Use an alternative format.
• * | eEfgG | For the conversions `o', increase the precision
• * | aAeEfgG | For the conversions `o', increase the precision
• | | until the first digit will be `0' if
• | | it is not formatted as complements.
• | | For the conversions `x',`X', `b' and`B'
• | | on non-zero, prefix the result with ``0x'',
• | | `0X'',`0b'' and ``0B'', respectively.
• * | | For `e',`E', `f',`g', and 'G',
• * | | For `a',`A', `e',`E', `f',`g', and 'G',
• | | force a decimal point to be added,
• | | even if no digits follow.
• | | For `g' and 'G', do not remove trailing zeros.
• ---------+---------------+-----------------------------------------
• + | bBdiouxX | Add a leading plus sign to non-negative
• * | eEfgG | numbers.
• * | aAeEfgG | numbers.
• | (numeric fmt) | For `o',`x', `X',`b' and `B', use
• | | a minus sign with absolute value for
• | | negative values. @@ -275,7 +279,7 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv)
• - | all | Left-justify the result of this conversion.
• ---------+---------------+-----------------------------------------
• 0 (zero) | bBdiouxX | Pad with zeros, not spaces.
• * | eEfgG | For `o',`x', `X',`b' and `B', radix-1
• * | aAeEfgG | For `o',`x', `X',`b' and `B', radix-1
• | (numeric fmt) | is used for negative numbers formatted as
• | | complements.
• ---------+---------------+----------------------------------------- @@ -983,6 +987,8 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) case 'G': case 'e': case 'E':
• case 'a':
• case 'A': { VALUE val = GETARG(); double fval;

--
NARUSE, Yui naruse@airemix.jp

=end

=begin

よって、そこにない 2 進や 8 進を独自に追加する必要を感じません。

いうことだと思います。全く必要ないとはいいませんが、あまり必要でないと

そもそもが C99 のリテラルなので、printf にこだわることも無いと思います。

じゃ、この提案自体必要なかったんじゃないかな。中途半端に Float を弄るの
は止めたほうがいいと思います。

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin

よって、そこにない 2 進や 8 進を独自に追加する必要を感じません。

いうことだと思います。全く必要ないとはいいませんが、あまり必要でないと

strtod(3) の解釈対象に含まれていない 2 進や 8 進を否定することが、
なぜ含まれている 16 進の否定につながるのでしょう。

そもそもが C99 のリテラルなので、printf にこだわることも無いと思います。

じゃ、この提案自体必要なかったんじゃないかな。中途半端に Float を弄るの
は止めたほうがいいと思います。

String#to_f の仕様・実装が下敷きとしていると思われる
strtod(3) の要素を 1 つマージすると言うことが中途半端だとは思いません。

--
NARUSE, Yui naruse@airemix.jp

=end

=begin

strtod(3) の解釈対象に含まれていない 2 進や 8 進を否定することが、
なぜ含まれている 16 進の否定につながるのでしょう。

これは to_f (と Float()) の話で、strtod(3) の話ではありません。成瀬さん
が認めたように、2進数も8進数もさほど重要だとは思われていなくて、今回の

と思います。

String#to_f の仕様・実装が下敷きとしていると思われる
strtod(3) の要素を 1 つマージすると言うことが中途半端だとは思いません。

strtod(3) に多少影響を受けていることはあるかもしれませんが、一番考慮さ
れているのは ruby での常識、ruby のリテラルだと思います。

が、strtod と同じであることを求めてるわけはないと思います。

また、結果的に他所の仕様が影響を与えることはよくあることですが、to_f が
strtod に従わなければならない理由にはならないと思いますよ。

「何故そうしたのか良くわからない、ああそうか、strtod(3) を参考にしたか
らこうなっているのか」「何故こうなっているの? -- それは strtod(3) と同
じものを提供しようとしたからです」そういう事なら分りますが、そういう話
じゃないですよね。

lib/scanf.rb で提供するほうが相応わしいと思います。

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin

strtod(3) の解釈対象に含まれていない 2 進や 8 進を否定することが、
なぜ含まれている 16 進の否定につながるのでしょう。

これは to_f (と Float()) の話で、strtod(3) の話ではありません。成瀬さん
が認めたように、2進数も8進数もさほど重要だとは思われていなくて、今回の

と思います。

なぜ同じなのでしょう。

String#to_f の仕様・実装が下敷きとしていると思われる
strtod(3) の要素を 1 つマージすると言うことが中途半端だとは思いません。

strtod(3) に多少影響を受けていることはあるかもしれませんが、一番考慮さ
れているのは ruby での常識、ruby のリテラルだと思います。

Ruby のリテラルは C 言語由来と思われるので、
これもやはり外しているとは思いません。
それとも、先に Ruby リテラルにこの形式を入れるべきという主張ですか?

が、strtod と同じであることを求めてるわけはないと思います。

また、結果的に他所の仕様が影響を与えることはよくあることですが、to_f が
strtod に従わなければならない理由にはならないと思いますよ。

「何故そうしたのか良くわからない、ああそうか、strtod(3) を参考にしたか
らこうなっているのか」「何故こうなっているの? -- それは strtod(3) と同
じものを提供しようとしたからです」そういう事なら分りますが、そういう話
じゃないですよね。

わたしの意図という話でしたら「同じものを提供しよう」とまでは言いませんが、
「参考に」はしています。

Ruby の仕様・実装という意味でしたら、r2457 からするに、
「同じものを提供しようとした」ように見えます。
ここで 16 進記法を解釈しなくなったのは、現状 "INF" や "NAN" なども

この点はまつもとさんに確認を取ります。

``` * util.c (ruby_strtod): use own strtod(3) implementation to avoid
locale hell.  Due to this change "0xff".to_f no longer returns 255.0
```

lib/scanf.rb で提供するほうが相応わしいと思います。

わたしは String#to_f は解釈すべきだと思っています。

--
NARUSE, Yui naruse@airemix.jp

=end

=begin

なぜ同じなのでしょう。

Ruby のリテラルは C 言語由来と思われるので、
これもやはり外しているとは思いません。
それとも、先に Ruby リテラルにこの形式を入れるべきという主張ですか?

それはよくわかりません。perl 経由のような気もします。仮に別にそうであっ
ても、C に従わなければならないこともないでしょう。

2進数や8進数も含めてそれが有用だから16進もある、場合によってはリテラル
もそうする、というなら Float() の仕様として個人的に納得できます。それは
そうですが、そうすべきだという主張ではありません。

そうでないなら、lib/scanf.rb でいいと思います。

Ruby の仕様・実装という意味でしたら、r2457 からするに、
「同じものを提供しようとした」ように見えます。
ここで 16 進記法を解釈しなくなったのは、現状 "INF" や "NAN" なども

この点はまつもとさんに確認を取ります。

で、別にそれで考えが変るわけじゃないんですよ。そりゃ、いろいろ言っても
まつもとさんの考えで決るとは思いますが。

strtod(3) を参考にしたり、影響されたりすることは普通にあるとは思います
が、strtod をこれ程までに尊敬している人は初めてです。

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin

(2010/03/26 8:20), EGUCHI Osamu wrote:

えぐち＠エスアンドイーです
＃おひさしぶりです

2010年3月24日2:55 NARUSE, Yuinaruse@airemix.jp:

String#to_f は従来から指数表記を許していたので、
String#hex などと使い分けている String#to_i 事情が違うと理解しています。

to_i では 0x をデファオルトでは数値として見ない、そして to_i はオプショ
ンを受け付けるということ。もしこれがあまり意味のないことだったら、to_f
というより to_i の仕様に疑問が生じます。hex は関係なく、'0xff'.to_i は
0 としかならないことに意味はないのか、ということです。意味のない使い分
けと断じていいですか。

to_i がデフォルトで prefix を見ないのは、0377 のような、
0 詰めした数値文字列を、8 進数表記として解釈してしまうのを

パターンが無いので、やはり事情が異なると思います。

のようなパターンで意図に反した解釈をする可能性があります。

このようなメソッドとの曖昧性で断念した記憶があります。

リテラルの場合は確かにご指摘の通りです。
なので、Ruby で 16 進浮動小数点数リテラルを導入するのは困難ですね。

to_f の場合には、"0x1" を 0 と解釈して欲しかった例と

--
NARUSE, Yui naruse@airemix.jp

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin

なぜ同じなのでしょう。

Ruby のリテラルは C 言語由来と思われるので、
これもやはり外しているとは思いません。
それとも、先に Ruby リテラルにこの形式を入れるべきという主張ですか?

それはよくわかりません。perl 経由のような気もします。仮に別にそうであっ
ても、C に従わなければならないこともないでしょう。

2進数や8進数も含めてそれが有用だから16進もある、場合によってはリテラル
もそうする、というなら Float() の仕様として個人的に納得できます。それは
そうですが、そうすべきだという主張ではありません。

つまり、ふなばさんは 16 進よりも 2 進や 8 進形式が好みであるところ、
16 進がそれらを差し置いて入るのは納得できないという事でしょうか。

Ruby の仕様・実装という意味でしたら、r2457 からするに、
「同じものを提供しようとした」ように見えます。
ここで 16 進記法を解釈しなくなったのは、現状 "INF" や "NAN" なども

この点はまつもとさんに確認を取ります。

で、別にそれで考えが変るわけじゃないんですよ。そりゃ、いろいろ言っても
まつもとさんの考えで決るとは思いますが。

strtod(3) を参考にしたり、影響されたりすることは普通にあるとは思います
が、strtod をこれ程までに尊敬している人は初めてです。

わたしの主張は、以下のようなものです。
(1) -h.hhh±pd 形式は、符号・仮数・指数という浮動小数点数をそのまま表しているため、

(2) -h.hhh±pd 形式は、C99 の浮動小数点数リテラルの一形式であり、
その形式は文法に、出力は printf に、解釈は strtod に記述されている
(3) 有用かつ安定した形式なので Ruby でも扱えるべきだ

--
NARUSE, Yui naruse@airemix.jp

=end

#### Updated by kosaki (Motohiro KOSAKI)over 10 years ago

=begin

strtod(3) を参考にしたり、影響されたりすることは普通にあるとは思います
が、strtod をこれ程までに尊敬している人は初めてです。

わたしの主張は、以下のようなものです。
(1) -h.hhh±pd 形式は、符号・仮数・指数という浮動小数点数をそのまま表しているため、

(2) -h.hhh±pd 形式は、C99 の浮動小数点数リテラルの一形式であり、
その形式は文法に、出力は printf に、解釈は strtod に記述されている
(3) 有用かつ安定した形式なので Ruby でも扱えるべきだ

kosakiです

については見解を提示してもらえませんでしょうか。有用なのは認めますが、非常にマイナーな表記形式であり、導入しても使用ユーザは小数にとどまると考えています。もっというと、うれしいのは一般ユーザじゃなくて見当違いのバグ報告にたいしてwont
fix といいやすくなるのが最大のメリットに見えます。

よって副作用があるなら lib/scanf.rb
に格下げというのは、ある意味現実的な判断のようにも思えます。これについて、どう判断しているのかを教えて欲しいです。

ふなばさんのメールについては趣旨が十分整理されていないように感じるので、賛成も反対も差し控えます。

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin
(2010/03/27 18:19), KOSAKI Motohiro wrote:

strtod(3) を参考にしたり、影響されたりすることは普通にあるとは思います
が、strtod をこれ程までに尊敬している人は初めてです。

わたしの主張は、以下のようなものです。
(1) -h.hhh±pd 形式は、符号・仮数・指数という浮動小数点数をそのまま表しているため、

(2) -h.hhh±pd 形式は、C99 の浮動小数点数リテラルの一形式であり、
その形式は文法に、出力は printf に、解釈は strtod に記述されている
(3) 有用かつ安定した形式なので Ruby でも扱えるべきだ

kosakiです

については見解を提示してもらえませんでしょうか。有用なのは認めますが、

もっというと、うれしいのは一般ユーザじゃなくて見当違いのバグ報告にたいしてwont
fix といいやすくなるのが最大のメリットに見えます。

よって副作用があるなら lib/scanf.rb
に格下げというのは、ある意味現実的な判断のようにも思えます。
これについて、どう判断しているのかを教えて欲しいです。

ML 上での最大の use case は浮動小数点数を理解していないユーザへの説明用でしょうね。

さて、えぐちさんの指摘は第一にはリテラルとしての導入は不可能という指摘だと思います。
それは仰るとおりです。

たぶん 0x1 を解釈して 1.0 が返るのを期待するのかなと思います。
しかし、String#to_f は 16 進を解さないので、現在 0.0 が返ります。
つまり、元々期待はずれだった結果がまた別の値になるわけです。
これは、当初の期待が誤っていると言えるでしょう。
よって、String#to_f についてはえぐちさんの指摘は当たらないと考えます。

--
NARUSE, Yui naruse@airemix.jp

=end

=begin

つまり、ふなばさんは 16 進よりも 2 進や 8 進形式が好みであるところ、
16 進がそれらを差し置いて入るのは納得できないという事でしょうか。

わたしの主張は、以下のようなものです。
(1) -h.hhh±pd 形式は、符号・仮数・指数という浮動小数点数をそのまま表しているため、

(2) -h.hhh±pd 形式は、C99 の浮動小数点数リテラルの一形式であり、
その形式は文法に、出力は printf に、解釈は strtod に記述されている
(3) 有用かつ安定した形式なので Ruby でも扱えるべきだ

ruby は C じゃないんで、直ちに ruby で採用しなければということにはらな
いでしょう。しかし、ruby でつかえてもいいと思います。で、一応 scanf と
いう対案を出しています。しかし、これまで strtod がどうのという話を繰り

るようです。僕も真相は知りませんが、普通に考えたら、

strtod が変化し始めた。自前の strtod を得ると例外なく16進数は扱えなくなっ
たが、使えていた事はむしろ予想外だったので、躊躇うこともなかった。

という事なのでは。

どっちにしろ、strtod はどうでもいいと思うのですが。

いう一点張りのようで、簡単には決着がつないだろうから、すでにリポジトリ
で変更されている分は一旦戻させて貰おうと思います (バグもあるようだし)。
ご了承下さい。

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin
(2010/03/27 20:26), EGUCHI Osamu wrote:

えぐち＠エスアンドイーです

2010年3月27日19:14 NARUSE, Yuinaruse@airemix.jp:

さて、えぐちさんの指摘は第一にはリテラルとしての導入は不可能という指摘だと思います。
それは仰るとおりです。

リテラル表現には（そのままの）aA 表現は導入不可能という点には

たぶん 0x1 を解釈して 1.0 が返るのを期待するのかなと思います。
しかし、String#to_f は 16 進を解さないので、現在 0.0 が返ります。
つまり、元々期待はずれだった結果がまた別の値になるわけです。
これは、当初の期待が誤っていると言えるでしょう。
よって、String#to_f についてはえぐちさんの指摘は当たらないと考えます。

String#to_f については '.’ + メソッド との整合性は必須でないので、

ただ、浮動小数点リテラル表現と String#to_f は同じであろうと

＃驚き最小の法則

こちらは、一貫性と利便性が衝突する事例という話だと思います。

で、現在同じかというと "10".to_f のような 10 進整数はすでに異なっています。
また、エッジケースを攻めると 4._9 はエラーになるが、
"4._9".to_f は 4.9 を返すというような例もあります。

「Ruby のリテラルとして浮動小数点数として解釈されるならば、
String#to_f でも解釈されるべき」は尊重されるべきだと思いますが、
その逆は必ずしも成り立つ必要はないと思います。

--
NARUSE, Yui naruse@airemix.jp

=end

#### Updated by kosaki (Motohiro KOSAKI)over 10 years ago

=begin

つまり、ふなばさんは 16 進よりも 2 進や 8 進形式が好みであるところ、
16 進がそれらを差し置いて入るのは納得できないという事でしょうか。

これだけだと「直感的に気に入らない。」としか読めないのでなんとか言語化してもらえませんか。

ちゃんと議論しておかないと、再提案の時に同じ話を蒸し返すことになって
お互い嫌な思いをするのではないでしょうか。

いう一点張りのようで、

ここは賛成しません。

それに賛成するかどうかはともかく、一点張りとは思いません。

ふなばさんのメールはそもそも言いたい事がよく分からないので返信が書きにくいです。

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin

つまり、ふなばさんは 16 進よりも 2 進や 8 進形式が好みであるところ、
16 進がそれらを差し置いて入るのは納得できないという事でしょうか。

String#to_i との統一性とバランス、でしょうか。

わたしの主張は、以下のようなものです。
(1) -h.hhh±pd 形式は、符号・仮数・指数という浮動小数点数をそのまま表しているため、

(2) -h.hhh±pd 形式は、C99 の浮動小数点数リテラルの一形式であり、
その形式は文法に、出力は printf に、解釈は strtod に記述されている
(3) 有用かつ安定した形式なので Ruby でも扱えるべきだ

ruby は C じゃないんで、直ちに ruby で採用しなければということにはらな
いでしょう。しかし、ruby でつかえてもいいと思います。で、一応 scanf と
いう対案を出しています。しかし、これまで strtod がどうのという話を繰り

strtod(3) の話は、16進整数表現の解釈はできないのかとの問いに対して、
strtod(3) の範囲ならば矛盾無く拡張できると返したのが最初です。

また、printf の反対なのだから scanf で対応するべきという主張に対しては、
16進浮動小数点数形式も浮動小数点数形式なので printf との対応にこだわらず、
String#to_f でも扱えていいと思うと返しています。

なお、先に scanf 側にも実装するべきとの主張ならばそうかもしれません。
と、思って lib/scanf.rb を見ると……、

def extract_float(s); s.to_f if s &&! skip; end

えーっと、lib/scanf.rb で使うというユースケースを String#to_f に

とりあえず Ruby で実装するなら以下の通りでしょうか。

diff --git a/lib/scanf.rb b/lib/scanf.rb
index ffc0d90..dd6ba6c 100644
--- a/lib/scanf.rb
+++ b/lib/scanf.rb
@@ -112,7 +112,7 @@ and tests/scanftests.rb for examples.)
[x,X]
Matches an optionally signed hexadecimal integer,

-[f,g,e,E]
+[a,e,f,g,A,E,F,G]
Matches an optionally signed floating-point number.

[s]
@@ -309,7 +309,22 @@ module Scanf

```  def skip;  /^\s*%\*/.match(@spec_string); end
```
• def extract_float(s); s.to_f if s &&! skip; end
• def extract_float(s)
• return nil unless s &&! skip
• if /\A(?[-+]?)0xXpP/ =~ s
• f1, f2 = frac.split('.')
• f = f1.hex
• if f2
• len = f2.length
• if len > 0
• f += f2.hex / (16.0 ** len)
• end
• end
• (sign == ?- ? -1 : 1) * Math.ldexp(f, exp.to_i)
• else
• s.to_f
• end
• end
def extract_decimal(s); s.to_i if s &&! skip; end
def extract_hex(s); s.hex if s &&! skip; end
def extract_octal(s); s.oct if s &&! skip; end
@@ -409,12 +424,12 @@ module Scanf
[ "([-+][0-7]{1,#{\$1.to_i-1}}|[0-7]{1,#{\$1}})", :extract_octal ]

```    # %f
```
• ```   when /%\*?[efgEFG]/
```
• ```     [ '([-+]?(?:\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))', :extract_float ]
```
• ```   when /%\*?[aefgAEFG]/
```
• ```     [ '([-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))', :extract_float ]

# %5f
```
• ```   when /%\*?(\d+)[efgEFG]/
```
• ```     [ '(?=[-+]?(?:\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))' +
```
• ```   when /%\*?(\d+)[aefgAEFG]/
```
• ```     [ '(?=[-+]?(?:0[xX](?:\.\h+|\h+(?:\.\h*)?)[pP][-+]\d+|\d+(?![\d.])|\d*\.\d*(?:[eE][-+]?\d+)?))' +
"(\\S{1,#{\$1}})", :extract_float ]

# %5s
```

@@ -491,7 +506,7 @@ module Scanf
:last_match_tried, :matched_count, :space

• SPECIFIERS = 'diuXxofFeEgGsc'

• SPECIFIERS = 'diuXxofFeEgGscaA'
REGEX = /
# possible space, followed by...
(?:\s*
diff --git a/test/scanf/test_scanf.rb b/test/scanf/test_scanf.rb
index 2ec4e54..169ffe6 100644
--- a/test/scanf/test_scanf.rb
+++ b/test/scanf/test_scanf.rb
@@ -276,6 +276,8 @@ module ScanfTests
[ "%g", "+3.25", [3.25] ],
[ "%G", "+3.25e2", [325.0] ],
[ "%f", "3.z", [3.0] ],

• ``` [ "%a", "0X1P+10", [1024.0] ],
```
• ``` [ "%A", "0x1.deadbeefp+99", [1.1851510441583988e+30] ],
```

# Testing embedded matches including literal '[' behavior
[",%d,%f", ",10,1.1", [10,1.1] ],

--
NARUSE, Yui naruse@airemix.jp

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin
(2010/03/27 21:46), EGUCHI Osamu wrote:

えぐち＠エスアンドイーです

2010年3月27日21:01 NARUSE, Yuinaruse@airemix.jp:

(2010/03/27 20:26), EGUCHI Osamu wrote:

2010年3月27日19:14 NARUSE, Yuinaruse@airemix.jp:

よって、String#to_f についてはえぐちさんの指摘は当たらないと考えます。
String#to_f については '.’ + メソッド との整合性は必須でないので、

ただ、浮動小数点リテラル表現と String#to_f は同じであろうと

＃驚き最小の法則

こちらは、一貫性と利便性が衝突する事例という話だと思います。

で、現在同じかというと "10".to_f のような 10 進整数はすでに異なっています。
また、エッジケースを攻めると 4._9 はエラーになるが、
"4._9".to_f は 4.9 を返すというような例もあります。

この挙動は厄介です。

Float(”4._9”) ではまた別のエラーになったりします。
どうあるべきだったのかについては明確な論点を持ってません。
ただ、小数点とメソッド呼び出しが曖昧になるケースという意味で

「Ruby のリテラルとして浮動小数点数として解釈されるならば、
String#to_f でも解釈されるべき」は尊重されるべきだと思いますが、
その逆は必ずしも成り立つ必要はないと思います。

それも踏まえて、、

Rubyリテラル表現としても使える表現形式を模索してからでも

なるのかがよくわかりません。

で、mame さんに教えてもらったのですが、IEEE 754:2008 の draft に、

```7.12.2 External hexadecimal character sequences representing finite numbers

Implementations supporting binary formats shall provide conversions between all
supported internal binary formats and external hexadecimal character sequences.
External hexadecimal character sequences for finite numbers are of the form
specified by ISO/IEC 9899, Programming Languages – C (C99) subclauses:
6.4.4.2 floating constants,
20.1.3 strtod,
7.19.6.2 fscanf (a, e, f, g), and
7.19.6.1 fprintf (a, A).
```

IEEE 754:2008 draft 1.2.9, 27 January 2007

Ruby は IEEE 754 に準拠しようという気はなかったはずなので、
この規定を守るいわれはありませんが、今後 IEEE 754:2008 をサポートしよう
という環境はこの形式の出力を行うことになります。
なお、C 言語 以外だと Java は　1.5 でこの形式をサポートしているようです。

というわけで、既にメジャーな 2 言語によってサポートされ、今後もサポート
する言語は増えることが予想されるので、リテラルとは独立にサポートして
いいのではないかと思います。

--
NARUSE, Yui naruse@airemix.jp

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin
(2010/03/27 23:49), Tanaka Akira wrote:

2010年3月27日19:14 NARUSE, Yuinaruse@airemix.jp:

ML 上での最大の use case は浮動小数点数を理解していないユーザへの説明用でしょうね。

その用途に必要なのは、String#to_f より、まず Float#to_s なんじゃないですかね。

printf でもいいですが。

なので、printf でいいんじゃないかなぁと思います。
Float#to_s(16) で期待するのがこの形式かというと、また少し悩むところがありますし。

なお、「ML 上での」とつけたのは観測可能だからでした。

それぞれの環境の Ruby にパッチを当てつつ、%a の出力をにらめっこしていました。
10 進だと 10 進変換が入るので何が起きているかわからないし、
pack だとどこまでが指数部か一目でわからないので、この形式は有用でした。

ついでに missing/vsnprint.f も含めた printf("%a") のパッチ。

diff --git a/LEGAL b/LEGAL
index 991bb4d..c28a0b5 100644
--- a/LEGAL
+++ b/LEGAL
@@ -123,6 +123,32 @@ win32/win32.[ch]:
You may distribute under the terms of either the GNU General Public

+util.c (partly):
+

• Copyright (c) 2004-2008 David Schultz das@FreeBSD.ORG
• Redistribution and use in source and binary forms, with or without
• modification, are permitted provided that the following conditions
• are met:
• 1. Redistributions of source code must retain the above copyright
• notice, this list of conditions and the following disclaimer.
• 2. Redistributions in binary form must reproduce the above copyright
• notice, this list of conditions and the following disclaimer in the
• documentation and/or other materials provided with the distribution. +
• THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
• ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
• IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
• ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
• FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
• DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
• OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
• HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
• LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
• OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
• SUCH DAMAGE.
+
random.c

This file is under the new-style BSD license.
diff --git a/missing/vsnprintf.c b/missing/vsnprintf.c
index 4e19651..acb09c9 100644
--- a/missing/vsnprintf.c
+++ b/missing/vsnprintf.c
@@ -559,7 +559,7 @@ BSD_vfprintf(FILE fp, const char *fmt0, va_list ap)
struct __suio uio; /
output information: summary /
struct __siov iov[NIOV];/
... and individual io vectors /
char buf[BUF]; /
space for %c, %[diouxX], %[eEfgG] */

• char ox[2]; /* space for 0x hex-prefix */

• char ox[4]; /* space for 0x hex-prefix, hexadecimal's 1. */
char *const ebuf = buf + sizeof(buf);
#if SIZEOF_LONG > SIZEOF_INT
long ln;
@@ -784,6 +784,11 @@ reswitch: switch (ch) {
base = 10;
goto number;
#ifdef FLOATING_POINT

• ``` case 'a':
```
• ``` case 'A':
```
• ```     if (prec >= 0)
```
• ```         prec++;
```
• ```     goto fp_begin;
case 'e':       /* anomalous precision */
case 'E':
if (prec != 0)
```

@@ -822,7 +827,12 @@ fp_begin: _double = va_arg(ap, double);
else
ch = 'g';
}

• ```     if (ch <= 'e') {    /* 'e' or 'E' fmt */
```
• ```     if (ch == 'a' || ch == 'A') {
```
• ```         --expt;
```
• ```         expsize = exponent(expstr, expt, ch + 'p' - 'a');
```
• ```         size = expsize + ndig;
```
• ```     }
```
• ```     else if (ch <= 'e') {   /* 'e' or 'E' fmt */
--expt;
expsize = exponent(expstr, expt, ch);
size = expsize + ndig;
```

@@ -1048,7 +1058,20 @@ long_len:
if ((flags & FPT) == 0) {
PRINT(cp, fieldsz);
} else { /* glue together f_p fragments */

• ```     if (ch >= 'f') {    /* 'f' or 'g' */
```
• ```     if (ch == 'a' || ch == 'A') {
```
• ```         ox[0] = '0';
```
• ```         ox[1] = ch + ('x' - 'a');
```
• ```         PRINT(ox, 2);
```
• ```         if (ndig > 1 || flags & ALT) {
```
• ```             ox[2] = *cp++;
```
• ```             ox[3] = '.';
```
• ```             PRINT(ox+2, 2);
```
• ```             PRINT(cp, ndig-1);
```
• ```         } else  /* XpYYY */
```
• ```             PRINT(cp, 1);
```
• ```         PRINT(expstr, expsize);
```
• ```     }
```
• ```     else if (ch >= 'f') {   /* 'f' or 'g' */
if (_double == 0) {
/* kludge for __dtoa irregularity */
if (ndig <= 1 &&
```

@@ -1112,6 +1135,7 @@ error:
#ifdef FLOATING_POINT

extern char BSD__dtoa __P((double, int, int, int *, int *, char *));
+extern char BSD__hdtoa(double, const char *, int, int *, int *, char *);

static char *
cvt(value, ndigits, flags, sign, decpt, ch, length, buf)
@@ -1135,7 +1159,14 @@ cvt(value, ndigits, flags, sign, decpt, ch, length, buf)
} else {
*sign = '\000';
}

• digits = BSD__dtoa(value, mode, ndigits, decpt, &dsgn, &rve);
• if (ch == 'a' || ch =='A') {
• digits = BSD__hdtoa(value,
• ch == 'a' ? "0123456789abcdef" : "0123456789ABCDEF",
• ndigits, decpt, &dsgn, &rve);
• }
• else {
• digits = BSD__dtoa(value, mode, ndigits, decpt, &dsgn, &rve);
• } memcpy(buf, digits, rve - digits); xfree(digits); rve = buf + (rve - digits); @@ -1181,7 +1212,7 @@ exponent(p0, exp, fmtch) for (; t < expbuf + MAXEXP; *p++ = *t++); } else {
• *p++ = '0';
• if (fmtch & 15) p++ = '0'; / other than p or P */ *p++ = to_char(exp); } return (int)(p - p0); diff --git a/sprintf.c b/sprintf.c index bec0569..e26b833 100644 --- a/sprintf.c +++ b/sprintf.c @@ -227,6 +227,10 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv)
• | equal to the precision, or in dd.dddd form otherwise.
• | The precision specifies the number of significant digits.
• G | Equivalent to `g', but use an uppercase`E' in exponent form.
• * a | Convert floating point argument as [-]0xh.hhhhp[+-]dd,
• * | which is consisted from optional sign, "0x", fraction part
• * | as hexadecimal, "p", and exponential part as decimal.
• * A | Equivalent to `a', but use uppercase`X' and `P'. *
• Field | Other Format
• ------+-------------------------------------------------------------- @@ -244,7 +248,7 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv)
• Flag | Applies to | Meaning
• ---------+---------------+-----------------------------------------
• space | bBdiouxX | Leave a space at the start of
• * | eEfgG | non-negative numbers.
• * | aAeEfgG | non-negative numbers.
• | (numeric fmt) | For `o',`x', `X',`b' and `B', use
• | | a minus sign with absolute value for
• | | negative values. @@ -255,19 +259,19 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv)
• | | sprintf string.
• ---------+---------------+-----------------------------------------
• # | bBoxX | Use an alternative format.
• * | eEfgG | For the conversions `o', increase the precision
• * | aAeEfgG | For the conversions `o', increase the precision
• | | until the first digit will be `0' if
• | | it is not formatted as complements.
• | | For the conversions `x',`X', `b' and`B'
• | | on non-zero, prefix the result with ``0x'',
• | | `0X'',`0b'' and ``0B'', respectively.
• * | | For `e',`E', `f',`g', and 'G',
• * | | For `a',`A', `e',`E', `f',`g', and 'G',
• | | force a decimal point to be added,
• | | even if no digits follow.
• | | For `g' and 'G', do not remove trailing zeros.
• ---------+---------------+-----------------------------------------
• + | bBdiouxX | Add a leading plus sign to non-negative
• * | eEfgG | numbers.
• * | aAeEfgG | numbers.
• | (numeric fmt) | For `o',`x', `X',`b' and `B', use
• | | a minus sign with absolute value for
• | | negative values. @@ -275,7 +279,7 @@ get_hash(volatile VALUE *hash, int argc, const VALUE *argv)
• - | all | Left-justify the result of this conversion.
• ---------+---------------+-----------------------------------------
• 0 (zero) | bBdiouxX | Pad with zeros, not spaces.
• * | eEfgG | For `o',`x', `X',`b' and `B', radix-1
• * | aAeEfgG | For `o',`x', `X',`b' and `B', radix-1
• | (numeric fmt) | is used for negative numbers formatted as
• | | complements.
• ---------+---------------+----------------------------------------- @@ -983,6 +987,8 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) case 'G': case 'e': case 'E':
• case 'a':
• case 'A':
{
VALUE val = GETARG();
double fval;
diff --git a/test/ruby/test_sprintf.rb b/test/ruby/test_sprintf.rb
index 15424d9..f8dbbd5 100644
--- a/test/ruby/test_sprintf.rb
+++ b/test/ruby/test_sprintf.rb
@@ -191,6 +191,19 @@ class TestSprintf < Test::Unit::TestCase
assert_equal(" Inf", sprintf("% 0e", 1.0/0.0), "moved from btest/knownbug")
end

• def test_float_hex

• assert_equal("-0x0p+0", sprintf("%a", -0.0))

• assert_equal("0x0p+0", sprintf("%a", 0.0))

• assert_equal("0x1p-1", sprintf("%a", 0.5))

• assert_equal("0x1p+0", sprintf("%a", 1.0))

• assert_equal("0x1p+1", sprintf("%a", 2.0))

• assert_equal("0x1p+10", sprintf("%a", 1024))

• assert_equal("0x1.23456p+789", sprintf("%a", 3.704450999893983e+237))

• assert_equal("0x1p-1074", sprintf("%a", 4.9e-324))

• end
+
BSIZ = 120

def test_skip
diff --git a/util.c b/util.c
index 9cdb563..88914be 100644
--- a/util.c
+++ b/util.c
@@ -3860,6 +3857,149 @@ ruby_each_words(const char str, void (*func)(const char, int, void*), void *ar
}
}

+/*-

• * Copyright (c) 2004-2008 David Schultz das@FreeBSD.ORG
• *
• * Redistribution and use in source and binary forms, with or without
• * modification, are permitted provided that the following conditions
• * are met:
• * 1. Redistributions of source code must retain the above copyright
• * notice, this list of conditions and the following disclaimer.
• * 2. Redistributions in binary form must reproduce the above copyright
• * notice, this list of conditions and the following disclaimer in the
• * documentation and/or other materials provided with the distribution.
• *
• * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
• * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
• * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
• * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
• * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
• * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
• * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
• * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
• * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
• * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
• * SUCH DAMAGE.
• / + +#define DBL_MANH_SIZE 20 +#define DBL_MANL_SIZE 32 +#define INFSTR "Infinity" +#define NANSTR "NaN" +#define DBL_ADJ (DBL_MAX_EXP - 2) +#define SIGFIGS ((DBL_MANT_DIG + 3) / 4 + 1) +#define dexp_get(u) ((int)(word0(u) >> Exp_shift) & ~Exp_msk1) +#define dexp_set(u,v) (word0(u) = (((int)(word0(u)) & ~Exp_mask) | (v << Exp_shift))) +#define dmanh_get(u) ((int)(word0(u) & Frac_mask)) +#define dmanl_get(u) ((int)word1(u)) + + +/
• * This procedure converts a double-precision number in IEEE format
• * into a string of hexadecimal digits and an exponent of 2. Its
• * behavior is bug-for-bug compatible with dtoa() in mode 2, with the
• * following exceptions:
• *
• * - An ndigits < 0 causes it to use as many digits as necessary to
• * represent the number exactly.
• * - The additional xdigs argument should point to either the string
• * "0123456789ABCDEF" or the string "0123456789abcdef", depending on
• * which case is desired.
• * - This routine does not repeat dtoa's mistake of setting decpt
• * to 9999 in the case of an infinity or NaN. INT_MAX is used
• * for this purpose instead.
• *
• * Note that the C99 standard does not specify what the leading digit
• * should be for non-zero numbers. For instance, 0x1.3p3 is the same
• * as 0x2.6p2 is the same as 0x4.cp3. This implementation always makes
• * the leading digit a 1. This ensures that the exponent printed is the
• * actual base-2 exponent, i.e., ilogb(d).
• *
• * Inputs: d, xdigs, ndigits
• * Outputs: decpt, sign, rve
• */ +char * +BSD__hdtoa(double d, const char *xdigs, int ndigits, int *decpt, int *sign,
• char **rve) +{
• U u;
• char *s, *s0;
• int bufsize;
• uint32_t manh, manl; +
• u.d = d;
• if (word0(u) & Sign_bit) {
• /* set sign for everything, including 0's and NaNs */
• *sign = 1;
• word0(u) &= ~Sign_bit; /* clear sign bit */
• }
• else
• *sign = 0; +
• switch (fpclassify(d)) {
• case FP_NORMAL:
• *decpt = dexp_get(u) - DBL_ADJ;
• break;
• case FP_ZERO:
• *decpt = 1;
• return (nrv_alloc("0", rve, 1));
• case FP_SUBNORMAL:
• u.d = 5.363123171977039e+154 / 0x1p514 */;
• *decpt = dexp_get(u) - (514 + DBL_ADJ);
• break;
• case FP_INFINITE:
• *decpt = INT_MAX;
• return (nrv_alloc(INFSTR, rve, sizeof(INFSTR) - 1));
• default: /* FP_NAN or unrecognized */
• *decpt = INT_MAX;
• return (nrv_alloc(NANSTR, rve, sizeof(NANSTR) - 1));
• } +
• /* FP_NORMAL or FP_SUBNORMAL */ +
• if (ndigits == 0) /* dtoa() compatibility */
• ndigits = 1; +
• /*
• * If ndigits < 0, we are expected to auto-size, so we allocate
• * enough space for all the digits.
• */
• bufsize = (ndigits > 0) ? ndigits : SIGFIGS;
• s0 = rv_alloc(bufsize); +
• /* Round to the desired number of digits. */
• if (SIGFIGS > ndigits && ndigits > 0) {
• float redux = 1.0;
• int offset = 4 * ndigits + DBL_MAX_EXP - 4 - DBL_MANT_DIG;
• dexp_set(u, offset);
• u.d += redux;
• u.d -= redux;
• *decpt += dexp_get(u) - offset;
• } +
• manh = dmanh_get(u);
• manl = dmanl_get(u);
• *s0 = '1';
• for (s = s0 + 1; s < s0 + bufsize; s++) {
• *s = xdigs[(manh >> (DBL_MANH_SIZE - 4)) & 0xf];
• manh = (manh << 4) | (manl >> (DBL_MANL_SIZE - 4));
• manl <<= 4;
• } +
• /* If ndigits < 0, we are expected to auto-size the precision. */
• if (ndigits < 0) {
• for (ndigits = SIGFIGS; s0[ndigits - 1] == '0'; ndigits--)
• ;
• } +
• s = s0 + ndigits;
• *s = '\0';
• if (rve != NULL)
• *rve = s;
• return (s0); +} + #ifdef __cplusplus #if 0 {

--
NARUSE, Yui naruse@airemix.jp

=end

=begin

これだけだと「直感的に気に入らない。」としか読めないのでなんとか言語化してもらえませんか。
(中略)

ふなばさんのメールはそもそも言いたい事がよく分からないので返信が書きにくいです。

to_f は strtod(3) ですって事でいいかな。こんなところで頑張っても俺的に
バランスを欠いてるし。

=end

=begin

String#to_i との統一性とバランス、でしょうか。

[ruby-dev:40728] で書いたようなことです。

たとえば、to_i は16進数を拒否できますが、to_f は選択肢がありません。両

ないですよ。失うものがあるように感じて、それが気になっているだけです。

は16進数も扱えますが、利用者はそのことについて忘れても問題ありません。

とって必要でなくても、to_i と to_f をつかう利用者から見れば、同じような

ます。これが統一性の話。また、失うものがあるとすれば、本当に、Float()
や to_f で提供する必要性、価値があるか、バランスのとれた解説方法なのか

あたりがバランスの話。

でも、まあ to_f は strtod(3) なんだろうだから、もういいと思います。これ
も理解してもらえるのかどうか分りませんがら。

=end

#### Updated by matz (Yukihiro Matsumoto)over 10 years ago

=begin
まつもと ゆきひろです

すいません、忙しくて放置してました。

In message "Re: [ruby-dev:40650] [Feature #2969] String#to_f が -h.hhh±pd を解釈できるように"
on Tue, 16 Mar 2010 04:01:52 +0900, Yui NARUSE redmine@ruby-lang.org writes:
|
|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.

|で、この形式を使っているのですが、RubyのString#to_f で解釈してくれず悲しくなるので、
|解釈できるようにしませんか。

• 1.9.2には導入しない。将来とも拒否というわけではない。

まず、この機能ですが、必要性はさほど高くないので、導入するの
であれば printf と同時に導入すべきだと思います。でも、実装が

に 1.9.2 に導入せずに、printfの実装も対にするべきだと思いま
す。

さて、時期の問題はともかくとして、最終的な仕様がどうあるべき
かについてですが、Rubyには「浮動小数点表現の解釈」を行う機能
は複数あります。

(a) 浮動小数点リテラル
(b) Float#to_f
(c) Float()
(d) lib/scanf.rb

で、今回導入検討している表現はメソッド呼び出しとコンフリクト
する可能性があるので (a) は対象にしないというのがコンセンサ
スだと思います。私も賛成します。

• Float()は表現に厳しく、間違った表現は積極的に例外にする
• to_f は適当に解釈し、間違った表現には0.0を返す

という違いがあります。さらに Integer() と to_i との類推からは、
「0x」などの解釈はFloatが行い、to_fは行わないということを期待
してもよいのではないかと思います。

とすると、将来 %a 表現の解釈を導入する際にはFloat()がこの形式
を解釈し、to_fは行わないというのが、自然ではないのかと思いま
す。もっとも、これは私の意見と言うだけで、最終決定ではありま
せんが。

scanf も %a を導入した方がよいと思います。

to_iやto_fがエラーに対して 0 を返すのは、後悔している仕様の

```                             まつもと ゆきひろ /:|)
```

p.s.
それはそれとして、"4._9".to_f が 4.9 なのは、うーん。

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin

(2010/03/29 7:04), Yukihiro Matsumoto wrote:

まつもと ゆきひろです

すいません、忙しくて放置してました。

In message "Re: [ruby-dev:40650] [Feature #2969] String#to_f が -h.hhh±pd を解釈できるように"
on Tue, 16 Mar 2010 04:01:52 +0900, Yui NARUSEredmine@ruby-lang.org writes:
|
|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.

|で、この形式を使っているのですが、RubyのString#to_f で解釈してくれず悲しくなるので、
|解釈できるようにしませんか。

• 1.9.2には導入しない。将来とも拒否というわけではない。

まず、この機能ですが、必要性はさほど高くないので、導入するの
であれば printf と同時に導入すべきだと思います。でも、実装が

に 1.9.2 に導入せずに、printfの実装も対にするべきだと思いま
す。

missing を含めた printf の実装は、[ruby-dev:40801] にあります。
ので、この点に関して時期的な問題はないかと思います。

さて、時期の問題はともかくとして、最終的な仕様がどうあるべき
かについてですが、Rubyには「浮動小数点表現の解釈」を行う機能
は複数あります。

(a) 浮動小数点リテラル
(b) Float#to_f
(c) Float()
(d) lib/scanf.rb

で、今回導入検討している表現はメソッド呼び出しとコンフリクト
する可能性があるので (a) は対象にしないというのがコンセンサ
スだと思います。私も賛成します。

• Float()は表現に厳しく、間違った表現は積極的に例外にする
• to_f は適当に解釈し、間違った表現には0.0を返す

という違いがあります。さらに Integer() と to_i との類推からは、
「0x」などの解釈はFloatが行い、to_fは行わないということを期待
してもよいのではないかと思います。

とすると、将来 %a 表現の解釈を導入する際にはFloat()がこの形式
を解釈し、to_fは行わないというのが、自然ではないのかと思いま
す。もっとも、これは私の意見と言うだけで、最終決定ではありま
せんが。

なるほど、わたしは手軽に使えればよいので異論はありません。

scanf も %a を導入した方がよいと思います。

scanf の実装は [ruby-dev:40799] に示しています。

to_iやto_fがエラーに対して 0 を返すのは、後悔している仕様の

--
NARUSE, Yui naruse@airemix.jp

=end

#### Updated by matz (Yukihiro Matsumoto)over 10 years ago

=begin
まつもと ゆきひろです

In message "Re: [ruby-dev:40816] Re: [Feature #2969] String#to_f が -h.hhh±pd を解釈できるように"
on Mon, 29 Mar 2010 08:08:37 +0900, "NARUSE, Yui" naruse@airemix.jp writes:

|> * 1.9.2には導入しない。将来とも拒否というわけではない。
|>
|> まず、この機能ですが、必要性はさほど高くないので、導入するの
|> であれば printf と同時に導入すべきだと思います。でも、実装が
|> 間に合わないので printf は提案しないということでしたら、無理
|> に 1.9.2 に導入せずに、printfの実装も対にするべきだと思いま
|> す。
|
|missing を含めた printf の実装は、[ruby-dev:40801] にあります。
|ので、この点に関して時期的な問題はないかと思います。

おお、それは見落としてました。でも、成瀬さんは「printfは1.9.3
まで待つ」とおっしゃっていたように思います。時期的な問題はな
いとすると、それはなぜ？

|> 最後に (d) scanf ですが、printfが %a を採用したあかつきには、
|> scanf も %a を導入した方がよいと思います。
|
|scanf の実装は [ruby-dev:40799] に示しています。

これについても時期的な問題はないわけですね。

|> to_iやto_fがエラーに対して 0 を返すのは、後悔している仕様の
|> 一つですが、今さら変えられないですね。
|
|次のチャンスは 2.0 ですかねぇ。

いやぁ、後悔しているからといって互換性問題を安易に導入しちゃ
うと、移行の妨げになっちゃいますからねえ。

```                             まつもと ゆきひろ /:|)
```

=end

#### Updated by naruse (Yui NARUSE)over 10 years ago

=begin

2010年3月29日9:08 Yukihiro Matsumoto matz@ruby-lang.org:

まつもと ゆきひろです

In message "Re: [ruby-dev:40816] Re: [Feature #2969] String#to_f が -h.hhh±pd を解釈できるように"
on Mon, 29 Mar 2010 08:08:37 +0900, "NARUSE, Yui" naruse@airemix.jp writes:

|> * 1.9.2には導入しない。将来とも拒否というわけではない。
|>
|> まず、この機能ですが、必要性はさほど高くないので、導入するの
|> であれば printf と同時に導入すべきだと思います。でも、実装が
|> 間に合わないので printf は提案しないということでしたら、無理
|> に 1.9.2 に導入せずに、printfの実装も対にするべきだと思いま
|> す。
|
|missing を含めた printf の実装は、[ruby-dev:40801] にあります。
|ので、この点に関して時期的な問題はないかと思います。

おお、それは見落としてました。でも、成瀬さんは「printfは1.9.3
まで待つ」とおっしゃっていたように思います。時期的な問題はな
いとすると、それはなぜ？

あぁ、そこを受けていたのですね、当時はもっと実装に時間がかかると思ったからです。

|> 最後に (d) scanf ですが、printfが %a を採用したあかつきには、
|> scanf も %a を導入した方がよいと思います。
|
|scanf の実装は [ruby-dev:40799] に示しています。

これについても時期的な問題はないわけですね。

はい。

|> to_iやto_fがエラーに対して 0 を返すのは、後悔している仕様の
|> 一つですが、今さら変えられないですね。
|
|次のチャンスは 2.0 ですかねぇ。

いやぁ、後悔しているからといって互換性問題を安易に導入しちゃ
うと、移行の妨げになっちゃいますからねえ。

その辺が 2.0 では議論が落ち着くだろう (いや、落ち着いて欲しい) と

to_f はそのままで、新明示的変換指示メソッドだけ変えるでもいいのでしょうが。

--
NARUSE, Yui
naruse@airemix.jp

=end

#### Updated by matz (Yukihiro Matsumoto)over 10 years ago

=begin
まつもと ゆきひろです

In message "Re: [ruby-dev:40820] Re: [Feature #2969] String#to_f が -h.hhh±pd を解釈できるように"
on Mon, 29 Mar 2010 09:33:27 +0900, "NARUSE, Yui" naruse@airemix.jp writes:

|> おお、それは見落としてました。でも、成瀬さんは「printfは1.9.3
|> まで待つ」とおっしゃっていたように思います。時期的な問題はな
|> いとすると、それはなぜ？
|
|あぁ、そこを受けていたのですね、当時はもっと実装に時間がかかると思ったからです。
|実際に必要だったのは関数一つの調整だったので、
|土日で終わり [ruby-dev:40801] での投稿につながるわけです。

• 私が提示した仕様で問題ないか
• それは 1.9.2 に含めるかどうか

ですね。前者は実際に利用することを考えておられる方が判断して
ください。後者は Yuguiさんの issue ですね。

|以前 [ruby-dev:39851] で to_hoge 周りの整理を仰っていましたが、
|その辺が 2.0 では議論が落ち着くだろう (いや、落ち着いて欲しい) と
|思っていまして、その場合はそのドサクサに紛れて変えられるかなーと。
|to_f はそのままで、新明示的変換指示メソッドだけ変えるでもいいのでしょうが。

そううまくいくかなあ。考えさせてください。

```                             まつもと ゆきひろ /:|)
```

=end

#### Updated by yugui (Yuki Sonoda)over 10 years ago

=begin
String#to_fやリテラルにはちょっと躊躇しますが、今回はそれは入らないと言うことですのでおおむね差し支えないと思います。

=end

Also available in: Atom PDF