Подтвердить что ты не робот

Почему отливка NaN до долгого результата дает правильный результат?

В приведенном ниже примере кода я делю на ноль, который, когда я перешагиваю его с помощью отладчика, (divend/divisor) дает бесконечность или NaN (если делитель равен нулю). Когда я произнес этот результат до конца, я получаю действительный результат, обычно что-то вроде -9223372036854775808. Почему это действие действует? Почему он не останавливает выполнение (например, исключает исключение), а не назначает произвольное значение?

double divisor = 0;
double dividend = 7;
long result = (long)(dividend / divisor);
4b9b3361

Ответ 1

Почему это действие действует?

Листинг действителен, если во время компиляции известно, что преобразование может быть успешным или всегда выполняется успешно. Броски являются незаконными, когда конверсия не может быть успешной. (Например, приведение закрытого типа к интерфейсу, который он не реализует.) Преобразование из double в long может быть успешным. Поэтому актерский состав действителен.

Почему он не останавливает выполнение (например, генерирует исключение), а не назначает произвольное значение?

Потому что вы не просили об исключении! Спецификация предельно ясна в отношении ожидаемого поведения. См. Раздел 6.2.1:

Для преобразования из float или double в интегральный тип обработка зависит от контекста проверки переполнения, в котором происходит преобразование:

В проверенном контексте преобразование выполняется следующим образом:

• Если значение операнда равно NaN или бесконечно, генерируется исключение System.OverflowException.

[...]

В неконтролируемом контексте преобразование всегда выполняется успешно и выполняется следующим образом.

• Если значение операнда NaN или бесконечное, результатом преобразования является неопределенное значение типа назначения.

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

Ответ 2

По умолчанию арифметика С# не отмечена, поэтому недопустимые операции не будут генерировать исключения.

Вы можете использовать блок checked, чтобы заставить среду выполнения проверять переполнение и исключать исключения, например:

checked {
    double divisor = 0;
    double dividend = 7;

    long result = (long)(dividend / divisor);
}

Обратите внимание, что будет небольшое снижение производительности.

Ответ 3

Поведение явно описано в спецификации языка С#, раздел 6.2.1:

Для преобразования из float или double в интегральный тип обработка зависит от контекста проверки переполнения (§7.5.12), в котором происходит преобразование:

В проверенном контексте преобразование выполняется следующим образом:

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

В неконтролируемом контексте преобразование всегда выполняется успешно и продолжается следующим образом.

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

Bold добавлен для акцента. У вас есть Бесконечность и беспрепятственный контекст. Значение, которое вы получаете, не указано. Используйте проверенное ключевое слово, чтобы сделать его бомбой.

Ответ 4

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

Ответ 5

Это зависит от типа данных. Различные типы данных будут иметь разные результаты для деления на ноль.

http://blogs.msdn.com/rafats/archive/2006/07/20/673337.aspx

и

http://www.eggheadcafe.com/software/aspnet/30920566/divide-by-zero-question.aspx

От Джона Скита на другом сайте (ссылка выше):

Словом, стандарты. По крайней мере, я подозреваю, что причина. float/double соответствуют стандартным правилам IEC 60559 для арифметики, включая деление на ноль, что приводит к "бесконечному" значению, а не выбрасывая исключение.

Десятичное число (или, по крайней мере, не должны - спецификация С# допускает возможность) поддерживают "бесконечную" значение, тогда как float/double do. Аналогичный decimal не имеет указанного NaN.

Разделить на нулевое объяснение: http://www.philforhumanity.com/How_to_Divide_by_Zero.html