Проверьте, является ли число с плавающей запятой целым числом

StackOverflow https://stackoverflow.com/questions/142252

  •  02-07-2019
  •  | 
  •  

Вопрос

Этот код работает (C # 3)

double d;
if(d == (double)(int)d) ...;
  1. Есть ли лучший способ сделать это?
  2. По посторонним причинам я хочу избежать двойного приведения, поэтому;какие хорошие способы существуют, кроме этого?(даже если они не так хороши)

Примечание: Несколько человек указали на (важный) момент, что == часто бывает проблематично переоценивать значение с плавающей запятой.В этом случае я ожидаю значений в диапазоне от 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"));
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top