Лучший (самый безопасный) способ конвертировать из двойного в int - PullRequest
14 голосов
/ 23 августа 2010

Мне любопытно, как лучше всего конвертировать double в int.Безопасность среды выполнения - моя главная задача (это не обязательно должен быть самый быстрый метод, но это будет моей дополнительной задачей).Я оставил несколько вариантов, которые могу предложить ниже.Может ли кто-нибудь взвесить, что является лучшей практикой?Есть ли лучшие способы сделать это, которых я не перечислил?

        double foo = 1;
        int bar;

        // Option 1
        bool parsed = Int32.TryParse(foo.ToString(), out bar);
        if (parsed)
        {
            //...
        }

        // Option 2
        bar = Convert.ToInt32(foo);

        // Option 3
        if (foo < Int32.MaxValue && foo > Int32.MinValue) { bar = (Int32)foo; }

Ответы [ 8 ]

25 голосов
/ 23 августа 2010

Я думаю, что ваш лучший вариант будет делать:

checked
{
    try
    {
        int bar = (int)foo;
    }
    catch (OverflowException)
    {
     ...          
    }
}

С Таблица явных числовых преобразований

"При преобразовании значения типа double или float в целочисленный тип значение усекается. Если полученное целое значение находится вне диапазона целевого значения, результат зависит от контекста проверки переполнения. В проверенном контексте создается исключение OverflowException, а в непроверенном контексте результатом является неопределенное значение типа назначения. "

Примечание: Опция 2 также выдает OverflowException при необходимости.

6 голосов
/ 23 августа 2010

Я предпочитаю вариант 2.

Одна вещь, которую вам нужно сделать, это проверить наличие исключений, хотя для подтверждения того, что это сработало, так же, как вы проверяете 'parsed' в варианте 1:

try
{
    bar = Convert.ToInt32(foo); 
}
catch(OverflowException)
{
    // no can do!
{

Если бы вы конвертировали строку и т. Д. Вместо double, вы могли бы вместо этого получить «FormatException».

Редактировать

Я изначально говорил, что вариант 2 не особолучше, чем вариант 1, который @ 0xA3 указал неправильно.Вариант 1 хуже, потому что он преобразуется в строку перед анализом в целое число, что означает, что он менее эффективен.Вы также не получите OverflowException, если double находится вне целочисленного диапазона (что вы можете или не можете хотеть) - хотя 'parsed' будет False в этом случае.

5 голосов
/ 23 августа 2010

Я понимаю, что это не совсем то, о чем просил ОП, но эта информация может быть полезна.

Вот сравнение (из http://www.dotnetspider.com/resources/1812-Difference-among-Int-Parse-Convert-ToInt.aspx)

        string s1 = "1234";
        string s2 = "1234.65";
        string s3 = null;
        string s4 = "12345678901234567890123456789012345678901234567890";

        int result;
        bool success;

        result = Int32.Parse(s1);      // 1234
        result = Int32.Parse(s2);      // FormatException
        result = Int32.Parse(s3);      // ArgumentNullException
        result = Int32.Parse(s4);      // OverflowException

        result = Convert.ToInt32(s1);      // 1234
        result = Convert.ToInt32(s2);      // FormatException
        result = Convert.ToInt32(s3);      // 0
        result = Convert.ToInt32(s4);      // OverflowException

        success = Int32.TryParse(s1, out result);      // 1234
        success = Int32.TryParse(s2, out result);      // 0
        success = Int32.TryParse(s3, out result);      // 0
        success = Int32.TryParse(s4, out result);      // 0
3 голосов
/ 23 августа 2010

Опция 3a, не использующая исключения, всегда возвращает значение:

    Int32 Convert(Double d)
    {
        if (d <= (double)Int32.MinValue)
            return Int32.MinValue;
        else if (d >= (double)Int32.MaxValue)
            return Int32.MaxValue;
        else 
            return (Int32)d;
    }
3 голосов
/ 23 августа 2010

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

3 голосов
/ 23 августа 2010

Я бы использовал второй вариант. Коротко, чисто и все работает.

Вы также можете заглянуть в класс BigInteger в .Net4, и вам не придется проверять переполнение.

double foo = 1;            
BigInteger bigint = new BigInteger(foo);
2 голосов
/ 23 августа 2010

Если вам действительно нужно выяснить, что-то пошло не так, используйте обычное приведение и проверьте результат.

int ToInt(double foo)
{
    int result = (int)foo;
    if (foo != result)
        throw new ArgumentException()

    return result;
}

Это гарантирует, что недопустимое преобразование не будет выполнено. Если все в порядке с округлением до ближайшего целого числа, используйте Math.Round и проверьте, находится ли результат в пределах 0,5. Это гарантирует, что ваш метод не получит NaN или бесконечность.

2 голосов
/ 23 августа 2010

Параметры (1) и (2) делают по существу одно и то же. Опция (1) дает вам блок if (parsed), тогда как опция (2) выдает ошибку для любого double, который не может быть представлен как int.

Опция (3), по сути, такая же, как опция (2), за исключением того, что у нее есть дополнительная MinValue / MaxValue проверка, которую другие не делают.

В итоге: эти три куска кода делают разные вещи. Опция (3) выглядит наиболее надежной в том смысле, что она имеет дополнительную проверку диапазона.

Редактировать: Если подумать, использовать @Ani checked трюк - вы получаете проверку диапазона бесплатно.

...