HideMath.dll の Format 関数No.02272
山紫水明 さん 09/01/27 21:10
 

秀丸担当さん,

 次のようなマクロを実行します。
//--------------------------------------------------------//
setfloatmode 1;
loaddll "HideMath.dll";
#a = 1.05;
$x2 = dllfuncstr( "Format", "%.1lf", #a );
message $x2;
freedll;
endmacro;
//--------------------------------------------------------//

小数点第2位以下が四捨五入されて 1.1 と表示されます。これは正常です。
この後 #a の値を変えてみます。すると次のような結果になります。
  1.05→1.1  1.15→1.1  1.25→1.3  1.35→1.4  1.45→1.5
  1.55→1.6  1.65→1.6  1.75→1.8  1.85→1.9  1.95→2.0
1.15 と1.65 の結果がおかしいようですが,再現しますでしょうか。
浮動小数点数版(統合版)Ver.7.10 です。

              山紫水明

[ ]
RE:02272 HideMath.dll の Format 関数No.02273
アルビレオ さん 09/01/27 22:51
 
アルビレオです。

>小数点第2位以下が四捨五入されて 1.1 と表示されます。これは正常です。
>この後 #a の値を変えてみます。すると次のような結果になります。
>  1.05→1.1  1.15→1.1  1.25→1.3  1.35→1.4  1.45→1.5
>  1.55→1.6  1.65→1.6  1.75→1.8  1.85→1.9  1.95→2.0
>1.15 と1.65 の結果がおかしいようですが,再現しますでしょうか。
>浮動小数点数版(統合版)Ver.7.10 です。

これは二進浮動小数点数の仕様ですよ。
一般的にコンピュータ内の実数表記は2進数と指数に変換され、
... 4の位 2の位 1の位 1/2の位 1/4の位 1/8の位 ...
といった形になっています。

こういう形式にすると厳密には 1.1 も正しい値を格納することはできず、正確
には十進表記すると 1.1000000000000001 という値になっています。この場合は
誤差がわずかなので丸められて表面化しないわけです。(秀丸の実数は64ビット
形式だから)
確かめてみると 1.15 の場合は 1.1499999999999999 になります。四捨五入する
と 1.1 ですね。
ちなみに 1.5 や 1.25 なら二進数に変換しても誤差が出ないのでこういうこと
はおこりません。(それぞれ 1+1/2、1+1/4になるから)

BCDという十進数に近い形式で格納する手法もありますが、これをやると速度が
非常に遅くなる上に三角関数や log などの関数ルーチンも別途用意しないとい
けないので、テキストエディタのマクロとしてはあまり現実的ではないでしょう。
これをやっても十進表記とのずれがなくなるだけで、演算精度はむしろ悪化しま
すし。

[ ]
RE:02273 HideMath.dll の Format 関数No.02274
秀丸担当 さん 09/01/28 09:32
 

アルビレオさんの言われる通り、浮動小数点数にはどうしても誤差ができるよう
です。

#a = dllfunc( "Floor", (#a * 10) + 0.5 ) / 10;

としてあらかじめ数値を四捨五入の演算をしておいたら、とりあえずサンプルの
数値ではうまくいきましたが、あらゆる数値で確実かどうかというと、ちょっと
自信が無いです。

[ ]
RE:02274 HideMath.dll の Format 関数No.02275
山紫水明 さん 09/01/28 20:54
 
 アルビレオさん,

> これは二進浮動小数点数の仕様ですよ。

 こういう方面での私の理解と知識の底の浅さがばれてしまいましたが,詳しく
解説いただきどうもありがとうございます。仕組みは何となく理解できました。

 秀丸担当さん,

 実は Format 関数を使うのは別のフォーラムでマクロを作成した今回が初めて
です。それでこれは四捨五入によるのかどうか,わからなかったのですが,試し
てみたらどうもそのようでした。
 少数点以下2桁目の「5」で,10個のうち2つ誤差が出るのはそれほど低い確
率でもないような気がします。四捨五入のことと誤差の可能性もヘルプにあった
方がいいような気がします。

>#a = dllfunc( "Floor", (#a * 10) + 0.5 ) / 10;
>としてあらかじめ数値を四捨五入の演算をしておいたら、とりあえずサンプルの
>数値ではうまくいきましたが、

 四捨五入の方法として 0.5 を加えた上で切り捨てるというのは,よく使ってい
ました。ただこうするのなら,あえて後で Format を使わなくても,
$b = str(dllfunc( "Floor", (#a * 10) + 0.5 ) / 10)
で十分なような気がします。
 Format 関数の "%.5lf" の表示法はこの 0.5 を加えての処理も含んでやるよ
うにできれば,もしかしたら現在より精度が上がるのではないかと思いました。
素人考えに過ぎませんが。

     では, (^^)/~
                                    山紫水明
                                    SANSHISUIMEI

[ ]
RE:02275 HideMath.dll の Format 関数No.02276
Iranoan さん 09/01/28 21:14
 
 山紫水明さん今日は、Iranoan です。
>  Format 関数の "%.5lf" の表示法はこの 0.5 を加えての処理も含んでやるよ
> うにできれば,もしかしたら現在より精度が上がるのではないかと思いました。
 HideMath.dll の関数は、PI() 等一部の例外は有りますが、Format() なら
sprintf() と、殆どが C の標準関数の単なるラッパーです。
 そう考えると、Format() だけ特別な処理をするとかえって混乱してしまい
ます。

 また %.5lf と 0.5 を加えるのは全く意味が違うし、四捨五入で万事 OK で
もありませんから、ユーザーが適宜使い分ければ良いと思います。

[ ]
RE:02276 HideMath.dll の Format 関数No.02278
山紫水明 さん 09/01/28 22:09
 
 Iranoanさん,コメントありがとうございます。

> HideMath.dll の関数は、PI() 等一部の例外は有りますが、Format() なら
>sprintf() と、殆どが C の標準関数の単なるラッパーです。

 多分そういうことだろうなとは思っていましたが。

> また %.5lf と 0.5 を加えるのは全く意味が違うし、

 ここはちょっと私の書き方が悪かったようです。たまたま,「.5」と「0.5」
が一致してしまったのですが,意図は小数点以下の表示桁の次の桁に「5」を加
える,たとえば小数点以下3桁表示なら,あらかじめ0.0005 を加えるような処
理を関数の中でやれたら,という主旨でした。でも,おっしゃるように C の標
準関数とは違ってしまいます。

     では, (^^)/~
                                    山紫水明
                                    SANSHISUIMEI

[ ]