Проверьте, является ли число с плавающей запятой целым числом
-
02-07-2019 - |
Вопрос
Этот код работает (C # 3)
double d;
if(d == (double)(int)d) ...;
- Есть ли лучший способ сделать это?
- По посторонним причинам я хочу избежать двойного приведения, поэтому;какие хорошие способы существуют, кроме этого?(даже если они не так хороши)
Примечание: Несколько человек указали на (важный) момент, что == часто бывает проблематично переоценивать значение с плавающей запятой.В этом случае я ожидаю значений в диапазоне от 0 до нескольких сотен, и предполагается, что они должны быть целыми числами (не целые числа являются ошибками), поэтому, если эти точки "не должны" быть проблемой для меня.
Решение
d == Math.Floor(d)
делает то же самое другими словами.
Примечание:Надеюсь, вы осознаете, что должны быть очень осторожны, когда занимаетесь подобными вещами;плавающие значения / удвоения очень легко накапливают незначительные ошибки, из-за которых точные сравнения (подобные этому) не выполняются без видимой причины.
Другие советы
Если ваш double является результатом другого вычисления, вы, вероятно, хотите что-то вроде:
d == Math.Floor(d + 0.00001);
Таким образом, если произошла небольшая ошибка округления, она все равно будет совпадать.
Я думаю, это сработало бы:
if (d % 1 == 0) {
//...
}
Я не могу ответить на специфичную для C # часть вопроса, но я должен указать, что вы, вероятно, упускаете общую проблему с числами с плавающей запятой.
Как правило, целочисленность не очень четко определена для чисел с плавающей точкой.По той же причине, по которой равенство не очень четко определено для чисел с плавающей точкой.Вычисления с плавающей запятой обычно включают как ошибки округления, так и ошибки представления.
Например, 1.1 + 0.6 != 1.7
.
Да, именно так работают числа с плавающей запятой.
Здесь, 1.1 + 0.6 - 1.7 == 2.2204460492503131e-16
.
Строго говоря, самое близкое к сравнению равенства, которое вы можете сделать с плавающими значениями, - это сравнить их с точностью до заданного значения.
Если этого недостаточно, вы должны работать с десятичным представлением чисел, с представлением чисел с плавающей запятой со встроенным диапазоном ошибок или с символьными вычислениями.
Если вы просто собираетесь преобразовать его, ответ Майка Ф. / Хота хорош, но не совсем отвечает на ваш вопрос.Если вы собираетесь на самом деле тестировать, и это действительно важно, я рекомендую вам реализовать что-то, что включает в себя допустимую погрешность.
Например, если вы рассматриваете деньги и хотите проверить наличие четных сумм в долларах, вы могли бы сказать (следуя шаблону Хота)::
if( Math.abs(d - Math.Floor(d + 0.001)) < 0.001)
Другими словами, возьмите абсолютное значение разницы между значением и его целочисленным представлением и убедитесь, что оно невелико.
Вам не нужен дополнительный (двойной) пакет.Это работает:
if (d == (int)d) {
//...
}
Используйте математику.Truncate()
Математически гарантируется корректная работа простого теста, такого как "x == floor(x)", для любого числа FP с фиксированной точностью.
Все разрешенные FP-кодировки фиксированной точности представляют различные вещественные числа, и поэтому для каждого целого числа x существует не более одной FP-кодировки фиксированной точности, которая точно соответствует ему.
Следовательно, для каждого целого числа x, которое МОЖЕТ быть представлено таким образом, мы обязательно имеем x == floor(x), поскольку floor(x) по определению возвращает наибольшее число FP y такое, что y <= 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;
}
}
Чтобы обеспечить точность двойника...
Math.Abs(d - Math.Floor(d)) <= 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"));