Как определить, является ли десятичное / двойное число целым числом?


225

Как мне узнать, является ли десятичное или двойное значение целым числом?

Например:

decimal d = 5.0; // Would be true
decimal f = 5.5; // Would be false

или

double d = 5.0; // Would be true
double f = 5.5; // Would be false

Поэтому я хотел бы знать , что это так , что я могу определить программно , если я хочу , чтобы вывести значение , используя .ToString("N0")или .ToString("N2"). Если нет десятичного значения, то я не хочу показывать это.

Ответы:


410

Для чисел с плавающей запятой, n % 1 == 0как правило, это способ проверить, есть ли что-нибудь после десятичной запятой.

public static void Main (string[] args)
{
    decimal d = 3.1M;
    Console.WriteLine((d % 1) == 0);
    d = 3.0M;
    Console.WriteLine((d % 1) == 0);
}

Вывод:

False
True

Обновление: как @Adrian Lopez упомянул ниже, сравнение с небольшим значениемepsilonотброситневерныевычисления с плавающей запятой. Поскольку вопрос касаетсяdoubleзначений, ниже будет более надежный ответ с вычислением с плавающей точкой :

Math.Abs(d % 1) <= (Double.Epsilon * 100)

96
Это работает, когда число начинается как целое число, но не обязательно, когда число является результатом некоторого вычисления с плавающей запятой. Как насчет чего-то вроде "(d% 1) <epsilon", где epsion - это небольшая ценность?
Адриан Лопес

9
Обидно, что лучшим ответом в этой теме является комментарий, а не принятый ответ. Хороший Адриан.
Starskythehutch

13
Я также думаю, что комментарий Адриана выше - лучший ответ. Чтобы поместить его совет в формальный код C #: if (Math.Abs ​​(n% 1) <Double.Epsilon) {// Делать что-то, если n является целым числом}.
Рубен Рамирес Падрон

7
ИМХО, оператор модуля и числа с плавающей запятой просто бесполезны. Предложенный код невероятно запутан, учитывая количество нажатий клавиш, и практически не будет работать за пределами языков .NET. Могу поспорить, что это также намного медленнее, чем правильный путь (который вообще не использует никакого деления). Правильный способ сделать это - использовать Math.Abs(d-(int)d) < double.Epsilon. Как будто мы все должны были учиться на первой неделе нашего первого урока программирования в колледже.
krowe2

9
На самом деле, поскольку вопрос задан, этот ответ правильный, а комментарии неправильные. ОП не хочет знать, является ли двойное число целым для математических целей, а скорее как его отобразить. Только точные целочисленные значения должны отображаться без десятичной точки. Кроме того, комментарий о том, что мод не полезен с плавающей запятой и не работает за пределами .NET, плохо информирован. И (int)dэто катастрофа, которая бросит исключение для большинства двойных значений.
Джим Балтер

49

Есть несколько способов сделать это. Например:

double d = 5.0;
bool isInt = d == (int)d;

Вы также можете использовать по модулю.

double d = 5.0;
bool isInt = d % 1 == 0;

Будет ли один из них быстрее, чем другой? Я хочу сделать это в контексте чувствительности к производительности.
Василий

@Basil - зависит от обстоятельств. Вы должны сделать немного времени для себя и судить.
Эрик Фанкенбуш

4
Math.Abs(d-(int)d) < double.Epsilonбезопаснее, чемd == (int)d
Reactgular

3
@MathewFoscarini - я думаю, ты в замешательстве. Он устанавливает его в false, потому что результат 16.1 - 6.1 не является int. Задача состояла в том, чтобы найти, является ли данное значение целым числом, а не то, что что-то, приблизительно равное целому, является целым.
Эрик Фанкенбуш

1
@MathewFoscarini - Да, int - это число без десятичного значения (или десятичного значения 0). 16.1-6.1 не создает десятичное значение 0, это очень маленькое ненулевое значение, которое вызвано причудами формата с плавающей точкой IEEE. Нет способа узнать, имеет ли число SUPPOSED десятичное значение или нет, поэтому допущение, что значение округления столь же неточно. Цель вопроса состояла в том, чтобы узнать, было ли число с плавающей запятой целым числом, а не было ли оно приблизительно целым числом.
Эрик Фанкенбуш

21

Как насчет этого?

public static bool IsInteger(double number) {
    return number == Math.Truncate(number);
}

Тот же код для decimal.

На самом деле Марк Байерс высказал хорошую мысль: это может быть не тем, чего вы действительно хотите. Если вас действительно волнует, является ли число, округленное до двух десятичных знаков, целым числом , вы можете сделать это вместо этого:

public static bool IsNearlyInteger(double number) {
    return Math.Round(number, 2) == Math.Round(number);
}

1
возможно, обновите свое решение и добавьте: && number <int.MaxValue && number> int.MinValue
Уолтер Вехоувен

12

В то время как предлагаемые решения работают на простых примерах, делать это в целом - плохая идея. Число может не быть целым числом, но когда вы пытаетесь отформатировать его, оно достаточно близко к целому числу, которое вы получаете 1.000000. Это может произойти, если вы выполните вычисление, которое в теории должно давать ровно 1, но на практике дает число, очень близкое, но не точно равное единице, из-за ошибок округления.

Вместо этого сначала отформатируйте его, и если ваша строка заканчивается точкой, за которой следуют нули, то удалите их. Есть также некоторые форматы, в которых вы можете автоматически использовать конечные нули. Это может быть достаточно для вашей цели.

double d = 1.0002;
Console.WriteLine(d.ToString("0.##"));
d = 1.02;
Console.WriteLine(d.ToString("0.##"));

Вывод:

1
1.02

@ Марк Звучит интересно. У вас есть пример формата, который удаляет конечные нули?
Джим Гёртс

Я согласен, что это безопаснее и что ОП, вероятно, должен делать, но это не ответ на более узкий (но более интересный) вопрос о том, имеет ли значение дробную часть или нет.
Клиффорд

3
@ Clifford: Я обычно стараюсь отвечать, основываясь на том, что лучше всего решить проблему ОП, а не на том, что написано в заголовке. Названия редко являются точным описанием проблемы.
Марк Байерс

+1 Согласитесь, что пытаться проверять числа с плавающей запятой или двойники, чтобы увидеть, могут ли они быть целыми, плохо из-за ошибок округления и точности.
Ромен Хиппо

1
Для использования денег вы, вероятно, хотели бы, чтобы 1.2 отображалось как 1.20, что не относится к предлагаемому решению. Любой берущий?
Кьелл Рилбе

10
bool IsInteger(double num) {
    if (ceil(num) == num && floor(num) == num)
        return true;
    else
        return false;
}

Проблема решения.

Редактировать: Pwned Марк Рушаков.


4
или простоreturn ceil(num) == num && floor(num) == num;
Брайан Расмуссен

13
или простоreturn ceil(num) == floor(num);
gregsdennis

4

Ответ Марка Рушакова может быть проще, но следующие также работают и могут быть более эффективными, поскольку не существует неявной операции деления:

     bool isInteger = (double)((int)f) == f ;

и

     bool isInteger = (decimal)((int)d) == d ;

Если вы хотите одно выражение для обоих типов, возможно,

     bool isInteger = (double)((int)val) == (double)val ;

4

Если верхняя и нижняя граница Int32имеет значение:

public bool IsInt32(double value)
{
    return  value >= int.MinValue && value <= int.MaxValue && value == (int)value;
}

Первый тест, затем приведение, как это, вызовет исключение, отличное от возврата false, возможно, обновит ваш ответ
Уолтер Веховен,

@ компьютер, да, хорошая мысль. Что касается броска на бросок, я думаю, это будет зависеть от настроек вашего проекта.
Навфал


2

Вы можете использовать форматирование String для типа double. Вот пример:

double val = 58.6547;
String.Format("{0:0.##}", val);      
//Output: "58.65"

double val = 58.6;
String.Format("{0:0.##}", val);      
//Output: "58.6"

double val = 58.0;
String.Format("{0:0.##}", val);      
//Output: "58"

Дайте мне знать, если это не поможет.


1
Это на самом деле не решает вопрос определения, если значение не имеет дробной части, что является математическим вопросом. Тем не менее, вероятно, что ОП нуждается в его пояснительной записке.
Клиффорд

2
Да, он хочет просто отформатировать двойное или десятичное значение без десятичной точки. Спасибо ...
BALKANGraph


0

Я сталкивался с подобной ситуацией, но где значение представляет собой строку. Пользователь вводит значение, которое должно быть в долларах, поэтому я хочу проверить, что оно числовое и имеет не более двух знаков после запятой.

Вот мой код для возврата true, если строка «s» представляет собой число с максимум двумя десятичными разрядами, и false в противном случае. Это позволяет избежать любых проблем, которые могут возникнуть в результате неточности значений с плавающей запятой.

try
{
    // must be numeric value
    double d = double.Parse(s);
    // max of two decimal places
    if (s.IndexOf(".") >= 0)
    {
        if (s.Length > s.IndexOf(".") + 3)
            return false;
    }
    return true;
catch
{
    return false;
}

Я обсуждаю это более подробно на http://progblog10.blogspot.com/2011/04/determining-whether-numeric-value-has.html .


4
Это предполагает, что вы работаете с одной культурой. Например, он не будет работать должным образом с культурами, которые представляют десятичные дроби, такие как 1.000,00
Джим Гёртс

0

Использование int.TryParse даст следующие результаты:

        var shouldBeInt = 3;

        var shouldntBeInt = 3.1415;

        var iDontWantThisToBeInt = 3.000f;

        Console.WriteLine(int.TryParse(shouldBeInt.ToString(), out int parser)); // true

        Console.WriteLine(int.TryParse(shouldntBeInt.ToString(), out parser)); // false

        Console.WriteLine(int.TryParse(iDontWantThisToBeInt.ToString(), out parser)); // true, even if I don't want this to be int

        Console.WriteLine(int.TryParse("3.1415", out  parser)); // false

        Console.WriteLine(int.TryParse("3.0000", out parser)); // false

        Console.WriteLine(int.TryParse("3", out parser)); // true

        Console.ReadKey();

-2

Возможно, не самое элегантное решение, но оно работает, если вы не слишком разборчивы!

bool IsInteger(double num) {
    return !num.ToString("0.################").Contains(".");
}

8
Это ужасное решение
Ospho

-2

Вы можете использовать метод TryParse.

int.TryParse()

Это проверяет, можно ли преобразовать значение в целое число. Затем результат может указывать флаг, который можно использовать в другом месте вашего кода.


Аргументом для int.TryParse является строка, а не double.
Джим Балтер

yourDouble.toString("G17")
BackDoorNoBaby

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.