実数の比較方法がわかりません。
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 〜
のようにすればいいのでしょうか?
大小関係の比較の場合はそのまんまでいいです。
「小さいかより等しい」ことを調べたいわけですから、厳密に等しくならなくても問題ないですよね?
(逆に、境界値をほんのわずか超えた値を「等しい」と判断してしまったらそちらの方が問題です)
なぜ等号の場合は=で比較しちゃいけないのか? という本質がまだ理解できていないように見受けられますが、
その辺は浮動小数点というものについて勉強すればおいおい分かってくると思います。
> 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
ということではないの?
実数を扱う場合は
桁落ち、有効数値と細かい制約があります。
これを機会に調べてみるといいです。
この現象は言語問わずなので、なにもDelphiで絞って検索する必要はありません。
procedure TForm1.Button1Click(Sender: TObject);
var
a:Single;
begin
a := 100000.9;
if (a = 100000.9) then
ShowMessage('同じ');
end;
変な例えになりますが、仮に5cmの長さの針金を選んでくださいと言われたとき
現実には5cmの目盛に少しの誤差もなくぴったり一致することはめったにないわけで、
ほんのわずか目盛の上か下に寄っているものも誤差として認めざるを得ないでしょう。
でも「5cm以下の長さ」と言われたら、あくまで目盛と合うかそれより下回っていることが条件なので
目盛より上に出ているのは認めなくていいでしょうということです。
1より小さい箇所はとても小さいから気にしない。
で、済むのなら=とか<で判断してもいいのでは^^;
それじゃ困ると言う場合は、困る桁までが整数になるように
10^n を掛けて、その後小数点以下を切り捨てて整数にし、
その整数で判断するというのはどうですか?
やりたいことですが
たとえば
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
になるので
無限に処理されてしまいます。
1.01:30 - 09:30
2.09:30 - 17:30
3.17:30 - 01:30
の間違いでした。
0.729166666666667 は 17:30 です。
> if Now >= StopTime then begin
Nowには日付部分も入ってるけど、TimeOf(Now)とかしなくていいの?
> TimeInfo.Stop = 0.729166666666667
> だったのが
> StopTime = 0.729166666664241
> になるので
代入で値が変わってしまっているとしたら比較以前の問題でしょう。
精度が合っていないのだと思いますが、それぞれの変数の型は何ですか?
>> 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;
日付・時刻型の一致検査にはSameTimeなどの関数が用意されていますね。
大小関係まで含めた比較だとCompareTimeというのがありますが、これも浮動小数点の誤差を考慮に入れてくれてるのかな?
どうもありがとうございます。
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 を使って処理するようにしました。
ツイート | ![]() |