小林です。

----- Original Message -----
From: "Wakou Aoyama" <wakou / fsinet.or.jp>
Subject: [ruby-math:00317] Re: Float#ceil, Float#floor, Float#round


>
> そうなんです。もちろん、BigFloat のような、より正確な仕組みを利用する
> 方が良いのは確かではありますが、Float でも十分な場合も多そうなんですよ
そうなんですよ。 BigFloat を作っておきながら、自分で言うのもなんですが、
現実は Float と Fixnum/Bignum で事足りるんですよね。 ^^;;;

> ね。そして、やはり sprintf() をはさむのは嬉しく無さそうなので、標準で
> も欲しいかなと。
>
どう標準化するのかが難しいですね。

丸め自体に誤差が入るので、「数値」として以後の計算にどれだけ意味が
あるのか私にはよくわかりません。
なら「指定した桁位置で丸めた文字列」を返すメソッド([ruby-math:00313]
的)? 「四捨五入」や「切捨て」機能を持つもの??

ちょっと暇つぶしに Float#round を、オプショナル引数が付けられるように変
更してみました。テストした範囲では、予想外に、結構いけるようです。
# 本来は to_s を変更すべき ?

新 round は引数なし、または引数の値がゼロか1のときは従来と同じ。
引数の値が正なら四捨五入、負なら切捨てになります。
(BigFloat#round と同じ)

numeric.c を以下のように修正.パッチでなくて申し訳ありません
(他にも独自修正があるので)。

1.numeric.c の最後のところを、以下のように変更
     rb_define_method(rb_cFloat, "round", flo_round, -1);

2.numeric.c のflo_round()を、以下のとそっくり入れ替える
static VALUE
flo_round(argc,argv,num)
 int   argc;
 VALUE *argv;
    VALUE num;
{
 VALUE vLoc;
 int   iLoc;
    double f = RFLOAT(num)->value;
    long val;

 if(rb_scan_args(argc,argv,"01",&vLoc)==0) {
  iLoc = 1;
 } else {
  Check_Type(vLoc, T_FIXNUM);
  iLoc = NUM2INT(vLoc);
  if(iLoc==0) iLoc = 1;
 }

 if(iLoc==1) {
  /* Same as before(returns an integer ) */

     if (f > 0.0) f = floor(f+0.5);
  if (f < 0.0) f = ceil(f-0.5);

  if (!FIXABLE(f)) {
   return rb_dbl2big(f);
  }
     val = f;
  return INT2FIX(val);
 } else {
  char   sz[32];
  float  v;
  int    fOff = iLoc;

  if(iLoc<0) iLoc = -iLoc;

  /* Round at iLoc position */
  sprintf(sz,"1.e%d",iLoc-1);
  sscanf(sz,"%f",&v);
  f *= v;
  if(fOff>0) {
   /* Round up */
   if (f > 0.0) f = floor(f+0.5);
   if (f < 0.0) f = ceil(f-0.5);
  } else {
   /* Round off */
   if (f > 0.0) f = floor(f);
   if (f < 0.0) f = ceil(f);
  }
  f /= v;
     return rb_float_new(f);
 }
}

小林 茂雄
shigeo / tinyforest.gr.jp