Автоматическое приведение от двойного к десятичному безопасно: безопасно ли следующее?


Безопасно ли приводить от двойника к десятичному в C#следующим образом:

int downtimeMinutes = 90;
TimeSpan duration = TimeSpan.FromHours(2d);
decimal calculatedDowntimePercent = duration.TotalMinutes > 0?
    (downtimeMinutes / (decimal)duration.TotalMinutes) * 100.0m : 0.0m;

Если ответ "да", то никакой суеты, я просто отмечу, как принято.

3 5

3 ответа:

Да, это безопасно, потому что десятичное число имеет большую точность

Http://msdn.microsoft.com/en-us/library/364x0z75 (VS.80).aspx

Компилятор будет вводить приведения вокруг других не десятичных чисел, но все они будут вписываться в десятичное число * (см. предостережение).

-- Предостережение

  • Decimal-это не тип с плавающей запятой. Его мандат заключается в том, чтобы всегда поддерживать точность. В то время как число с плавающей запятой, такое как double (которое я в основном использую) , делает компромисс на точность для размещения очень больших чисел). Очень большие или очень маленькие числа не укладываются в десятичную дробь. Поэтому Лизе нужно спросить себя, будет ли величина операции, вероятно, меньше 28 значимых цифровых цифр. 28 значащих цифр достаточно для большинства сценариев.

  • Плавающая точка хороша для астрономически больших или инфинитессимально малых чисел... или операции между ними, которые дают достаточную точность. Я должен посмотреть это, но двойной-это нормально для плюс или минус несколько миллиардов с точностью до нескольких десятичных знаков (до 7 или 8?).

  • В науке нет смысла измерять дальше точности вашего оборудования. В финансах часто логическим выбором является double, потому что double вычислительно более эффективен для большинства ситуаций (иногда они хотят немного большей точности, но эффективность не стоит выбрасывать на что-то вроде decimal). В конце концов, мы все должны стать прагматичными и сопоставить потребности бизнеса с цифровой домен. Существуют инструменты, которые имеют динамическое представление чисел. Вероятно, в .net есть библиотеки для того же. Однако стоит ли оно того? Иногда это так. Часто это перебор.

В целом, double -> decimal преобразования небезопасны, потому что decimal имеет меньший диапазон.

Однако, пока TotalMinutes меньше максимального значения decimal, это будет нормально. Это верно, потому что TimeSpan.MaxValue.TotalMinutes < (double)decimal.MaxValue (я считаю, что TimeSpan использует long внутренне.)

Итак: да.

*: (79,228,162,514,264,337,593,543,950,335 минут-это 1,1×10^13 раз больше возраста Вселенной)

Нет , вообще говоря, приведение от двойника к десятичному не всегдабезопасно :

[TestCase(double.MinValue)]
[TestCase(double.MaxValue)]
[TestCase(double.NaN)]
[TestCase(double.NegativeInfinity)]
[TestCase(double.PositiveInfinity)]
public void WillFail(double input)
{
    decimal result = (decimal)input; // Throws OverflowException!
}

Как пояснил ОП в комментарии к вопросу, " безопасное" Бытие " не вызывает исключений времени выполнения", выше показано, что исключения могут возникать при приведении двойника к десятичному числу.


Выше приведен общий ответ, за которым многие гуглеры могли бы прийти сюда. Однако, чтобы также ответить на конкретный вопрос OP, вот сильный указание на то, что код не будет выдавать исключений, даже в крайних случаях:
[Test]
public void SpecificCodeFromOP_WillNotFail_NotEvenOnEdgeCases()
{
    int downtimeMinutes = 90;
    foreach (TimeSpan duration in new[] {
        TimeSpan.FromHours(2d), // From OP
        TimeSpan.MinValue,
        TimeSpan.Zero,
        TimeSpan.MaxValue })
    {
        decimal calculatedDowntimePercent = duration.TotalMinutes > 0 ?
            (downtimeMinutes / (decimal)duration.TotalMinutes) * 100.0m : 0.0m;
    }
}