Як визначити, чи десятковий / подвійний - це ціле число?


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
IMHO, оператор модуля та числа з плаваючою комою просто не поєднуються корисними. Запропонований код неймовірно заплутаний, враховуючи кількість використовуваних натискань клавіш, і він працюватиме практично ніде поза мовою .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
Реакційний

3
@MathewFoscarini - я думаю, ти розгублений. Він встановлює значення false, оскільки результат 16.1 - 6.1 не є цілим. Сенс полягав у тому, щоб визначити, чи задане значення є цілим, а не, якщо щось, що є приблизно, є цілим.
Ерік Функенбуш

1
@MathewFoscarini - Так, int - це число без десяткового значення (або десяткове значення 0). 16.1-6.1 не створює значення десяткової величини, це дуже мале ненульове значення, яке викликається хитками формату IEEE з плаваючою точкою. Неможливо дізнатися, чи число ПОДАЄТЬСЯ мати десяткове значення чи ні, тож припустити, що значення округлення так само неточно. Метою питання було знати, чи число плаваючої точки є цілим числом, а не приблизно це ціле число.
Ерік Функенбуш

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
Walter Vehoeven

12

Хоча запропоновані рішення працюють на простих прикладах, але це взагалі погана ідея. Число може бути не цілим числом, але коли ви намагаєтесь його відформатувати, воно досить близьке до цілого числа, яке ви отримаєте1.000000 . Це може статися, якщо ви зробите розрахунок, який теоретично повинен дати рівно 1, але на практиці дає число, дуже близьке до, але не зовсім рівне одному, через помилки округлення.

Натомість відформатуйте його спочатку, і якщо ваш рядок закінчується через період, за яким слід нулі, тоді зніміть їх. Існує також кілька форматів, за допомогою яких ви можете використовувати цю смужку з нулями автоматично. Це може бути досить добре для ваших цілей.

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

Вихід:

1
1.02

@Mark Звучить цікаво. Чи є у вас приклад формату, який знімає нулі?
Джим Гюртс

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

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

+1 Погодьтеся, що спроба тестування плавців чи парних пар, щоб побачити, чи можуть вони бути інтами, погана через помилки округлення та точності.
Ромен Гіппо

1
Для використання грошей, можливо, ви хочете, щоб 1,2 відображалися як 1,20, що не стосується запропонованого рішення. Хто-небудь, хто приймає?
Kjell Rilbe

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

Проблемне рішення.

Редагувати: Поклав Марк Рушаков.


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;
}

Перший тест, а потім цей випадок, він би кинув виняток, окрім повернення помилкового, можливо, оновіть свою відповідь
Вальтер Веховен

@комп'ютер, так, хороший момент. Що стосується кидка на ролях, я думаю, це залежатиме від налаштувань вашого проекту.
nawfal


2

Ви можете використовувати формат String для подвійного типу. Ось приклад:

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
Jim Geurts

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
Це жахливе рішення
Осфо

-2

Ви можете скористатися методом "TryParse".

int.TryParse()

Це перевіряє, чи можна перетворити значення на ціле ціле число. Потім результат може вказувати прапор, який можна використовувати в іншому місці вашого коду.


Аргумент для int.TryParse - це рядок, а не подвійний.
Джим Балтер

yourDouble.toString("G17")
BackDoorNoBaby

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.