浮動小数点数が整数かどうかをテストします
-
02-07-2019 - |
質問
このコードは動作します(C#3)
double d;
if(d == (double)(int)d) ...;
- これを行うより良い方法はありますか?
- 外部からの理由から、二重キャストを避けたいのです。これ以外にどんな素晴らしい方法がありますか? (たとえそれらがそれほど良くないとしても)
注:何人かの人々は、==が浮動小数点のリグレードに問題があることが多い(重要な)ポイントを指摘しました。この場合、私は0から数百の範囲の値を期待し、それらは整数であると想定されています(非整数はエラーです)。私にとっては問題になります。
解決
d == Math.Floor(d)
言い換えれば同じことをします。
NB:この種のことをするときは非常に注意しなければならないことを知っていることを願っています。 floats / doublesは非常に簡単に小さなエラーを蓄積するため、明確な理由もなく正確な比較(このようなエラー)が失敗します。
他のヒント
doubleが別の計算の結果である場合、おそらく次のようなものが必要です。
d == Math.Floor(d + 0.00001);
そのように、わずかな丸め誤差があった場合でも、それは一致します。
これはうまくいくと思う:
if (d % 1 == 0) {
//...
}
質問のC#固有の部分に答えることはできませんが、浮動小数点数に関する一般的な問題がない可能性が高いことを指摘する必要があります。
一般に、整数値は浮動小数点数では明確に定義されていません。同じ理由で、floatでは平等が適切に定義されていません。浮動小数点の計算には通常、丸め誤差と表現誤差の両方が含まれます。
たとえば、1.1 + 0.6 != 1.7
。
はい、それは浮動小数点数が機能する方法です。
ここで、1.1 + 0.6 - 1.7 == 2.2204460492503131e-16
。
厳密に言えば、floatでできる等値比較に最も近いのは、それらを選択した精度で比較することです。
これで十分でない場合は、10進数表現、組み込みの誤差範囲を持つ浮動小数点数表現、またはシンボリック計算で作業する必要があります。
変換するだけの場合、Mike F / Khothの答えは良いですが、あなたの質問にはまったく答えません。実際にテストするつもりで、それが実際に重要な場合は、エラーのマージンを含むものを実装することをお勧めします。
たとえば、お金を検討していて、偶数ドルの金額をテストしたい場合は、次のように言うことができます(Khothのパターンに従って):
if( Math.abs(d - Math.Floor(d + 0.001)) < 0.001)
つまり、値と整数表現の差の絶対値を取得し、小さいことを確認します。
そこに余分な(二重)は必要ありません。これは動作します:
if (d == (int)d) {
//...
}
Math.Truncate()を使用
「x == floor(x)」などの単純なテストは、固定精度のFP数に対して正しく機能することが数学的に保証されています。
すべての有効な固定精度FPエンコーディングは、異なる実数を表します。したがって、整数xごとに、正確に一致する固定精度FPエンコーディングが最大で1つあります。
したがって、floor(x)は定義によってy <!> lt; = xおよびyは整数を表します。そのため、floor(x)はxを返す必要があります。
これにより、浮動小数点ドリフトを考慮して、求めている精度(プラスまたはマイナスのティックの半分)を選択できます。比較も不可欠です。これは素晴らしいことです。
static void Main(string[] args)
{
const int precision = 10000;
foreach (var d in new[] { 2, 2.9, 2.001, 1.999, 1.99999999, 2.00000001 })
{
if ((int) (d*precision + .5)%precision == 0)
{
Console.WriteLine("{0} is an int", d);
}
}
}
そして出力は
2 is an int
1.99999999 is an int
2.00000001 is an int
このようなもの
double d = 4.0;
int i = 4;
bool equal = d.CompareTo(i) == 0; // true
これを使用できますか
bool IsInt(double x)
{
try
{
int y = Int16.Parse(x.ToString());
return true;
}
catch
{
return false;
}
}
doubleの精度を処理するには...
Math.Abs(d - Math.Floor(d)) <= double.Epsilon
less then double.Epsilon の値がゼロとして比較できない場合、次のケースを考慮してください。
// number of possible rounds
const int rounds = 1;
// precision causes rounding up to double.Epsilon
double d = double.Epsilon*.75;
// due to the rounding this comparison fails
Console.WriteLine(d == Math.Floor(d));
// this comparison succeeds by accounting for the rounding
Console.WriteLine(Math.Abs(d - Math.Floor(d)) <= rounds*double.Epsilon);
// The difference is double.Epsilon, 4.940656458412465E-324
Console.WriteLine(Math.Abs(d - Math.Floor(d)).ToString("E15"));