実数の条件判断をするには?

解決


超初心者  2009-05-20 18:24:24  No: 34457

実数の比較方法がわかりません。

a: double;
b: double;

ネットで調べると

if a = b then 〜

ではなくて

if fabs(a - b) < 0.00001 

のようにするとなっていました。
等号比較の場合はネット上でわかったtのですが
それでは

if a <= b then 〜

の場合はどうすればいいのでしょうか

if ( fabs(a - b) < 0.00001 ) or ((a - b) < 0 )) then 〜

のようにすればいいのでしょうか?


ママ  2009-05-20 20:44:58  No: 34458

大小関係の比較の場合はそのまんまでいいです。
「小さいかより等しい」ことを調べたいわけですから、厳密に等しくならなくても問題ないですよね?
(逆に、境界値をほんのわずか超えた値を「等しい」と判断してしまったらそちらの方が問題です)

なぜ等号の場合は=で比較しちゃいけないのか? という本質がまだ理解できていないように見受けられますが、
その辺は浮動小数点というものについて勉強すればおいおい分かってくると思います。


はて?  2009-05-20 22:09:55  No: 34459

> if a <= b then 〜

(等幅フォントで見てください)

b=a の判定の場合 Abs(a - b) < 0.00001 なので
    0.00001    0    -0.00001
-------+*******+*******+-------+

b>a の判定の場合 (b - a) が
    0.00001    0    -0.00001
-------+-------+*******+*******+**...

両方を組み合わせて
    0.00001    0    -0.00001
-------+*******+*******+*******+**...

以上より
if (b-a) < 0.00001 then 

ということではないの?


monaa  2009-05-20 22:43:48  No: 34460

実数を扱う場合は
桁落ち、有効数値と細かい制約があります。
これを機会に調べてみるといいです。
この現象は言語問わずなので、なにもDelphiで絞って検索する必要はありません。
procedure TForm1.Button1Click(Sender: TObject);
var
  a:Single;
begin
  a := 100000.9;
  if (a = 100000.9) then
    ShowMessage('同じ');
end;


ママ  2009-05-20 22:50:27  No: 34461

変な例えになりますが、仮に5cmの長さの針金を選んでくださいと言われたとき
現実には5cmの目盛に少しの誤差もなくぴったり一致することはめったにないわけで、
ほんのわずか目盛の上か下に寄っているものも誤差として認めざるを得ないでしょう。
でも「5cm以下の長さ」と言われたら、あくまで目盛と合うかそれより下回っていることが条件なので
目盛より上に出ているのは認めなくていいでしょうということです。


Manbon  2009-05-21 00:46:00  No: 34462

1より小さい箇所はとても小さいから気にしない。
で、済むのなら=とか<で判断してもいいのでは^^;

それじゃ困ると言う場合は、困る桁までが整数になるように
10^n を掛けて、その後小数点以下を切り捨てて整数にし、
その整数で判断するというのはどうですか?


超初心者  2009-05-21 01:28:53  No: 34463

やりたいことですが

たとえば

1.01:00 - 09:00
2.09:00 - 17:00
3.17:00 - 01:00

のように時間を区切って
Listを作成しました。

判断するところは

CheckTime := StopTime;
for i := 1 to List.Count do begin
  TimeInfo = TTimeInfo(List.Items[i - 1])
  if (EndTime >= TimeInfo.Start) and (EndTime < TimeInfo.Stop) then
  begin
    StartTime := TimeInfo.Start;
    StopTime  := TimeInfo.Stop;
  end;
end;

タイマーを利用して時間を見に行って

if Now >= StopTime then begin
  //  時間帯が切り替わったときの処理
  //  判断処理を呼び出す
end;

といった処理ですが
TimeInfo.Stop = 0.729166666666667
だったのが
StopTime = 0.729166666664241
になるので
無限に処理されてしまいます。


超初心者  2009-05-21 01:31:31  No: 34464

1.01:30 - 09:30
2.09:30 - 17:30
3.17:30 - 01:30

の間違いでした。

0.729166666666667 は 17:30 です。


tor  2009-05-21 01:45:05  No: 34465

> if Now >= StopTime then begin
Nowには日付部分も入ってるけど、TimeOf(Now)とかしなくていいの?

> TimeInfo.Stop = 0.729166666666667
> だったのが
> StopTime = 0.729166666664241
> になるので
代入で値が変わってしまっているとしたら比較以前の問題でしょう。
精度が合っていないのだと思いますが、それぞれの変数の型は何ですか?


おっとっと  2009-05-21 02:18:22  No: 34466

>> if Now >= StopTime then begin
私なら下記のようにやっちゃいますが

var
 ChkTime:string;
 

ChkTime := FormatDateTime('HH:MM',Time);
if (chkTime >=  '01:30') and (ChkTime <  '09:30') then 
begin
  処理
end
else if (ChkTime >=  '09:30') and (ChkTime <  17:30) then 
begin
  処理
end
else
begin
  処理
end;


ちなみに  2009-05-21 03:02:13  No: 34467

日付・時刻型の一致検査にはSameTimeなどの関数が用意されていますね。
大小関係まで含めた比較だとCompareTimeというのがありますが、これも浮動小数点の誤差を考慮に入れてくれてるのかな?


超初心者  2009-05-21 17:36:31  No: 34468

どうもありがとうございます。

tar さん
精度って型のことでしょうか?
型はすべて TDateTime です。
ちなみに
 0.729166666666667 も 0.729166666664241 も
同じ  17:30 になります。

おっとっとさん
文字列で比較できるのですか?
17:30 - 01:30
この場合は2つに分けるんでしょうかね?
if ((ChkTime >= '17:30') and (ChkTime <  24:00)) or
   ((ChkTime >= '00:00') and (ChkTime <  01:30)) then

CompareTime というのを見つけました。
ちなみにさんありがとうございます。
CompareTime のソース見てみました。
誤差補正しているようです。
A <= B の判断は (CompareTime(A,B) = 0) で
できるみたいです。

いくつか試してみて問題なかったので
CompareTime を使って処理するようにしました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加