初めまして。
次のような計算を行った場合
function A(s: Extended): Variant;
begin
Result := s;
end;
var
d1, d2: double;
begin
d1 := double( A(10.2) );
d2 := double( A(100) );
ShowMessage( IntToStr(Trunc( d1 * d2 )) );
end;
計算結果が "1020" になるところ "1019"になって返ってしまいます。
Variant変数とTruncを絡めた計算であり、ある一部の値の組み合わせで
正しい結果が得られなくなります。
なぜだか教えて頂けないでしょうか。宜しくお願い申し上げます。
とりあえず...
ShowMessage( IntToStr(Round( d1 * d2 )) );
もしくは...
ShowMessage( FormatFloat('#',d1 * d2));
で正しい結果になると思います。
これは、実数型であればDelphiに限らず起こる現象です。
d1 * d2は、1020ですが内部では1019.9999...となっています。
Truncは、それを単純に整数に変換するため1019となります。
Roundは、それの近似値を計算して整数に変換するので1020となります。
d1,d2をExtended型にすると精度が高くなるので回避できるかもしれませんがVariantに代入すると内部的にいったんDoubleに変換されるようなのでダメみたいです。(推測です。)
実数型は、多かれ少なかれ誤差を生じるものなので注意してください。可能であれば整数型が金額型を使ったほうが良いと思います。
Halbow です。
> Variant変数とTruncを絡めた計算であり、
Variant は関係ありません。
procedure TForm1.Button1Click(Sender: TObject);
var
d1, d2: double;
begin
d1 := 10.2;
d2 := 100;
ShowMessage( IntToStr(Trunc( d1 * d2 )) );
ShowMessage(Format('%.14f',[d1*d2]));
end;
これでなぜ、Trunc() で 1019 になるか分かります。
ふつうは、もっとも近い整数にするのなら Round() を
使うと思います。
ご意見ありがとうございます。
実数型をそのままTrunc関数に放り込みことが危険だということは
承知いたしました。
ただ目的は切捨てで、変数には先ほどの値が入るとは限らないのです。
ですので別の方法を考えないといけないという結論になりました。
とりあえず、実数型を別の型に変換してからTrunc関数を使うように
してみますが、もし他に良い方法があればご教授のほう宜しくお願いします。
結果以下の関数で対処することにしました。
ありがとうございました。
function _Trunc(X: Extended): Integer;
var
s: String;
begin
s := FloatToStr(X);
Result := Trunc(StrToFloat(s));
end;
これでどうでしょう?
function A(s: Extended): Variant;
begin
Result := s; // 文字列に変換した方が良いかも?
end;
var
d1, d2: Extended;
begin
d1 := StrToFloat(A(10.2));
d2 := StrToFloat(A(100));
ShowMessage( IntToStr(Trunc( d1 * d2 )) );
end;
d1,d2をExtendedにしてA()を文字列と見なしStrToFloatで変換しています。
ちなみに
d1 := Extended(A(10.2));
とするとd1には10.2が入りません。
ということは、A(10.2)の戻り値は、Extended型で10.2が正しく入っているようなのでExtendedにキャストするときに
Extended (10.2)
↓(ここで丸め誤差発生)
Double (10.199999...)
↓
Extended (10.199999...)
という処理をしているようです。
で、StrToFloatを使うと
Extended (10.2)
↓(丸め誤差は発生しない)
String ("10.2")
↓
Extended (10.2)
正しくD1に代入できるようです。
Halbow です。
> function _Trunc(X: Extended): Integer;
> var
> s: String;
> begin
> s := FloatToStr(X);
> Result := Trunc(StrToFloat(s));
> end;
ふーむ、SysUtils.pas を読むと、文字列 <-> 浮動小数点 の変換は、結局
指定された桁数で丸めているだけでは? これと Trunc() を使いたいという
のはどこか矛盾しているように思います。 例えば、0.0000000001 を加えて
から Trunc() する方が理にかなっているかも知れませんね。
少数以下が1桁でしたら、固定小数点型のCurrency型を使えば良いかと思います。
ツイート | ![]() |