喫茶XRAYトップページ

[スレッド全体
]

[8078] 計算結果をバイト値で確認返信 削除
2014/10/9 (木) 12:59:10 Mr.XRAY
>一方,SimpleRoundTo 関数は,元の値を使用して四捨五入します.
>そのために違いが生じることになります.


SimpleRoundTo 関数で使用する元の値を確認してみます.
元の値とは計算式による値のことです.
Delphi XE2 で実装された浮動小数点用レコードで計算結果の値をバイト値
で確認してみました.
図のように,e の値が 1 だけ小さい値になっています.
つまり,FloatToStr(SimpleRoundTo) の結果は,計算結果を正確に反映していると言えます.

[Delphi のデータ型 - RAD Studio]
http://docwiki.embarcadero.com/RADStudio/XE6/ja/Delphi_%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B#.E6.B5.AE.E5.8B.95.E5.B0.8F.E6.95.B0.E7.82.B9.E3.83.87.E3.83.BC.E3.82.BF.E5.9E.8B

で,Extended 型のレコード型は TExtended80Rec なので,これを使用しています.
動作確認は,Windows 7 U6(SP1) + Delphi XE5 Pro (VCL-32) です.


//-----------------------------------------------------------------------------
//  (1)と(2)の計算式は,数学的には等しく,179.935となる
//  SimpleRoundToの使用にはusesにMathが必要
//  Delphi XE2 で実装された浮動小数点用レコードで計算結果の値を確認
//-----------------------------------------------------------------------------
procedure TForm1.Button1Click(Sender: TObject);
var
  a,b,c,d,e,f:Extended;
   FF: TExtended80Rec;
   I: Integer;
   StrText : string;
 begin
  a:=389.7;
  b:=94.4;
  c:=30;
  d:=0.35;
  e:=(a+b+c)*d;    //(1)
  f:=a*d+b*d+c*d;  //(2)

  FF := TExtended80Rec(e);
  StrText := '';
  for I := 9 downto 0 do begin
    StrText := StrText + IntToHex(FF.Bytes[I], 2) + ' ';
  end;

  FF := TExtended80Rec(f);
  StrText := StrText + sLineBreak + '';
  for I := 9 downto 0 do begin
    StrText := StrText + IntToHex(FF.Bytes[I], 2) + ' ';
  end;

  StrText := StrText + sLineBreak + sLineBreak +
             FloatToStr(e)+#13+
             FloatToStr(f)+#13+ sLineBreak +
             FloatToStr(SimpleRoundTo(e,-2))+#13+
             FloatToStr(SimpleRoundTo(f,-2));

  MessageBox(Handle, PChar(StrText), '結果', MB_OK);
end;



[8079] Re:計算結果をバイト値で確認返信 削除
2014/10/9 (木) 14:27:31 Mr.XRAY
> つまり,FloatToStr(SimpleRoundTo の結果は,計算結果を正確に反映していると言えます.

   FloatToStr(e)
   FloatToStr(f)
 
は 179.935 になります.
既に述べたように,FloatToStr が有効桁数 15 で丸めをするためと
考えられます.
つまり,今回のサンプルでは,計算結果の違いが 16 桁目以上にあることになります.
実際に [8078] でそのことを確認しました.

[System.SysUtils.FloatToStr - RAD Studio API Documentation]
http://docwiki.embarcadero.com/Libraries/XE6/ja/System.SysUtils.FloatToStr

一方,SimpleRoundTo は,引数の値としても元の値を用いようとします.
その結果ということになります.
当然ですが,以下のように,SimpleRoundTo の丸めの位置を小数点 3 桁目にすると
下図のような結果となります.
 
  FloatToStr(SimpleRoundTo(e,-3)+#13+
  FloatToStr(SimpleRoundTo(f,-3));


[8080] Re2:計算結果をバイト値で確認返信 削除
2014/10/9 (木) 14:37:20 Mr.XRAY
>したがって,以下のように,SimpleRoundTo の丸めの位置を小数点 3 桁目にすると
>下図のような結果となります.


以下のようなコードでも確認できます.
小数点以下,何桁目で丸めをするかということと,
何桁目までを表示するかということとは違うことにも注意します.


procedure TForm1.Button1Click(Sender: TObject);
var
  a,b,c,d,e,f:Extended;
  StrText : string;
 begin
  e:= 179.9349;
  f:= 179.9350;

  StrText := FloatToStr(e) + sLineBreak +
             FloatToStr(f) + sLineBreak + sLineBreak +
             FloatToStr(SimpleRoundTo(e,-2)) + sLineBreak +
             FloatToStr(SimpleRoundTo(f,-2)) + sLineBreak + sLineBreak +
             FloatToStr(SimpleRoundTo(e,-3)) + sLineBreak +
             FloatToStr(SimpleRoundTo(f,-3));

  MessageBox(Handle, PChar(StrText), '結果', MB_OK);
end;

179.9349 を小数点 2 桁目で丸めると,179.93 となるのは,
小数点 3 桁目の値が 4 (5 よりも小さい) だからです.
最初に示したサンプルコードでの e の計算結果は,実際には以下のようになっています.
  179.93499999999....
少なくても仮数部 (有効桁数) の 15 桁目までは 9 ということになります.


[8081] というわけで...返信 削除
2014/10/9 (木) 14:53:27 Mr.XRAY
最初に示したサンプルの結果は,結局,

[890_計算誤差 ( 数値計算の誤差 ) と多倍長演算]
http://mrxray.on.coocan.jp/Delphi/plSamples/890_CalcError.htm

の記事の内容が原因ということが分かりました.
たまにある質問なのですが,現在のコンピュータが有限桁数の演算しか
できないというこを理解していないと,数学的に正しい結果でないことが,
疑問となることがあります.

実際問題として,最後の桁の数値が 1 ぐらい違っていても,
数値計算では誤差範囲内と言えます.

ネタ元はメールでの問い合わせですが,例によって,いろいろやってみました.
質問された方が納得するかどうかは不明です (^^;


[8083] Re:というわけで...返信 削除
2014/10/9 (木) 22:33:50 Mr.XRAY
Delphi Forum に,内容をまとめて投稿しておきました.
[数値計算の誤差と四捨五入]
http://ht-deko.minim.ne.jp/delphiforum/?vasthtmlaction=viewtopic&t=1203.0#postid-2404

このような数値計算の誤差による違いを表現できる例というのは,
中々見つかるものではありません.
個人的には貴重なサンプルではないかと思っています.


[8082] Re:計算結果をバイト値で確認返信 削除
2014/10/9 (木) 15:47:49 Mr.XRAY
>Delphi XE2 で実装された浮動小数点用レコードで計算結果の値をバイト値
>で確認してみました.


Double 型であれば,FormatFloat を使用すれば実際の値を確認できます.
数学的な値は,197.935 です.
しかし,これがコンピュータによる計算の結果なのです.
当然ですが,どちらも,小数点 2 桁で四捨五入する(丸めると)
179.93 となります.


procedure TForm1.Button1Click(Sender: TObject);
var
  a,b,c,d,e,f:Double;
  strText : String;
begin
  a:=389.7;
  b:=94.4;
  c:=30;
  d:=0.35;
  e:=(a+b+c)*d;    //(1)
  f:=a*d+b*d+c*d;  //(2)

  StrText :=  FormatFloat('000.0000000000000000', e) + sLineBreak +
              FormatFloat('000.0000000000000000', f);
  MessageBox(Handle, PChar(strText), '結果', MB_OK);
end;



[▼次のスレッド]
INCM/CMT
Cyclamen v3.82