From: "H.Yamamoto" Date: 2004-05-14T14:34:41+09:00 Subject: [ruby-dev:23507] Re: [BUG] numeric.c (flo_to_s) broken? 山本です。 ////////////////////////////////////////////////////////////// matz@ruby-lang.org (Yukihiro Matsumoto) wrote: (2004/05/14 12:07) >えーと、内心、ここまでやるならsprintfと文字列処理ではなく、 >mantissaとか取り出して自前で処理すべきではと考えつつも、知識 >がないので手を出すのに躊躇しています。 直接、浮動小数点数のバイナリを操作するということでしょうか? [ruby-dev:20036]を読むと、相当難しそうですね・・・ ////////////////////////////////////////////////////////////// nobu.nakada@nifty.ne.jp wrote: (2004/05/14 12:10) >多少余っても問題ないでしょうし、 > > buf[sizeof(double)*CHAR_BIT*3/10+sizeof(int)*3+20] > >でいいんじゃないでしょうか。 私も最初、sizeof(double) から double の桁数が求まると思ったのですが、 http://www.ep.sci.hokudai.ac.jp/~yukiko/work/gtool/netCDF.htm によると IEEE形式ではない浮動小数点バイナリも存在するらしく、浮動小数点数が どう格納されているか保証がないので、DBL_DIG を見ないといけない気がしました。 >sizeof(int)*3の意図がよく分かりませんが。 整数の桁数より大きい数を意図しています。n バイトの場合、 整数は最大で 2 ** (8n) なので、桁数は log10(2 ** (8n)) + 1 = 8n * log10(2) + 1 < 2.5n + 1 とかやって求めました。 sizeof(int) == 1 => 256 => 3桁 ( <= 1 * 3) sizeof(int) == 2 => 65536 => 5桁 ( <= 2 * 3) sizeof(int) == 3 => 16777216 => 8桁 ( <= 3 * 3) ////////////////////////////////////////////////////////////// akira yamada / やまだあきら wrote: (2004/05/14 12:49) >これを適用すると値が負の場合にまずいようです(1.8系で試しました)。 > > Float(-0.1).to_s #=> "0.11" > Float(0.1).to_s #=> "0.1" すみません、符号を無視してました。 Index: numeric.c =================================================================== RCS file: /ruby/ruby/numeric.c,v retrieving revision 1.108 diff -u -w -b -p -r1.108 numeric.c --- numeric.c 10 May 2004 08:23:13 -0000 1.108 +++ numeric.c 14 May 2004 04:59:24 -0000 @@ -3,7 +3,7 @@ numeric.c - $Author: matz $ - $Date: 2004/05/10 08:23:13 $ + $Date: 2004/05/07 08:44:15 $ created at: Fri Aug 13 18:33:09 JST 1993 Copyright (C) 1993-2003 Yukihiro Matsumoto @@ -489,10 +489,9 @@ static VALUE flo_to_s(flt) VALUE flt; { - char buf[32]; - char *fmt = "%.15f"; + char fmt[sizeof(int)*3+10], *buf; double value = RFLOAT(flt)->value; - double avalue, d1, d2; + int exp; char *p, *e; if (isinf(value)) @@ -500,13 +499,27 @@ flo_to_s(flt) else if(isnan(value)) return rb_str_new2("NaN"); - avalue = fabs(value); - if (avalue < 1.0e-7 || avalue >= 1.0e15) { - fmt = "%.15e"; - } + buf = ALLOCA_N(char, DBL_DIG+sizeof(int)*3+20); + sprintf(fmt, "%%.%de", DBL_DIG - 1); sprintf(buf, fmt, value); - if (!(e = strchr(buf, 'e'))) { - e = buf + strlen(buf); + e = strchr(buf, 'e'); + exp = atoi(e+1); + if (-7 <= exp && exp < DBL_DIG - 1) { + char *d = strchr(buf, '.'); + if (exp > 0) { + memmove(d, d+1, exp); + d[exp] = '.'; + } + if (exp < 0) { /* needs room in `buf' for shifting */ + int n = -exp; + d[0] = d[-1]; + memmove(d+n, d, strlen(d)+1); + e += n; + p = (*buf == '-') ? buf+1 : buf; + memset(p, '0', n+1); + p[1] = '.'; + } + *e = '\0'; } p = e; while (*--p=='0')