Почему этот метод возвращает double.PositiveInfinity, а не DivideByZeroException?

Я запустил следующий фрагмент в интерактивном режиме VS2015 C# и получил очень странное поведение.

> double divide(double a, double b)
. {
.     try
.     {
.         return a / b;
.     }
.     catch (DivideByZeroException exception)
.     {
.         throw new ArgumentException("Argument b must be non zero.", exception);
.     }
. }    
> divide(3,0)
Infinity    
> 3 / 0
(1,1): error CS0020: Division by constant zero
> var b = 0;
> 3 / b
Attempted to divide by zero.
> 

Почему метод возвращал бесконечность, в то время как 3/0 выдавал ошибку, а 3/b выдавал форматированную ошибку? Могу ли я заставить деление выдать ошибку вместо возврата бесконечности?

Если я переформатирую метод в

double divide(double a, double b)
{
    if ( b == 0 )
    {
        throw new ArgumentException("Argument b must be non zero.", new DivideByZeroException());
    }
    return a / b;
}

будет ли новое исключение DivideByZeroException содержать ту же информацию и структуру, что и перехваченное исключение?


person Kelson Ball    schedule 25.07.2016    source источник


Ответы (4)


Это потому, что вы используете System.Double.

Как указано в MSDN DivideByZeroException выбрасывается только для целочисленных типов и Decimal.

Это потому, что трудно определить «так называемый» ноль для значения Double.

PositiveInfinity также получается в результате деления на ноль с положительным делимым, а NegativeInfinity получается в результате деления на ноль с отрицательным делимым. (источник: снова MSDN)

DivideByZeroException не подходит для типов с плавающей запятой. Примечание. Вы можете получить NaN при попытке деления на ноль с нулевым делимым.

person Sergey.quixoticaxis.Ivanov    schedule 25.07.2016
comment
Но почему для двойных значений используются PositiveInfinity, NegativeInfinity или NaN, а не исключения? Это означает, что код дает сбой молча, а вычисления продолжаются (и эти значения распространяются), и следствием этого является то, что а) вывод, вероятно, будет мусором, б) пользователь может даже не знать о наличии проблемы (особенно если код является фоновым, автоматическим процессом на сервере) и c) если проблема обнаружена, будет утомительно отслеживание ее до ее первого появления в коде (и может быть много мест, где она может возникнуть). - person patrickjlee; 03.05.2019
comment
@patrickjlee вы назвали это неудачей, но это не обязательно рассматривается как таковое. Все зависит от задачи. ИМХО Single и Double часто неправильно используются при реализации бизнес-логики: эти типы, будучи неточными, по моему опыту, приводят к гораздо большим проблемам, если их не обрабатывать осторожно. - person Sergey.quixoticaxis.Ivanov; 03.05.2019
comment
@sergey-quixoticaxis-ivanov Я пишу финансовое программное обеспечение более 30 лет, и я никогда не сталкивался с ситуацией, когда было бы уместно, чтобы программное обеспечение продолжало работать после того, как в результате были сгенерированы Infinity, -Infinity или NaN. расчета. Такая ситуация почти всегда является результатом неожиданной ситуации с данными или ошибки в коде, поэтому пользователь должен быть предупрежден о наличии проблемы. Таким образом, текущее поведение по умолчанию (в .NET Core 3.0 и более ранних версиях и .NET 4.72 и более ранних версиях), на мой взгляд, является неприятностью для таких приложений. - person patrickjlee; 11.05.2019
comment
@patrickjlee Мне очень нравятся комментарии о 30 годах написания финансового программного обеспечения. Я рад за вас, если вы использовали неточные типы с плавающей запятой без полного упорядочения все эти годы и никогда не стреляли себе в ногу. Но все финансовое программное обеспечение, которое я видел (и я должен сказать, что я видел только программное обеспечение торговых столов), использует точные типы (и не написано на C#). C# имеет хороший тип decimal, который предназначен для задач, связанных с деньгами. Для сценариев, не связанных с деньгами, таких как научные вычисления и 3D-моделирование всех видов, бесконечность и NaN довольно распространены. - person Sergey.quixoticaxis.Ivanov; 11.09.2020

Почему метод возвращал бесконечность, в то время как 3/0 выдавал ошибку, а 3/b выдавал форматированную ошибку?

Потому что в первом случае 0 не целое число, а двойное. В то время как во втором - целое число. Здесь вы должны понимать, что double — это число с плавающей запятой. Таким образом, у него нет точного значения, такого как целое число. С другой стороны, целое число может быть представлено компьютером со 100% точностью.

Здесь вы можете найти очень хорошую статью о числах с плавающей запятой.

person Christos    schedule 25.07.2016

Как уже говорилось выше, это связано с тем, что вы используете двойное значение в качестве делителя. Вы можете доказать это, используя пример с вашими переменными, но используя double вместо var.

> double a = 3;
> double b = 0;
> a/b
∞
person Rich Linnell    schedule 25.07.2016
comment
Спасибо, что указали на это, я забыл, что использовал var. Указание 0 как двойного литерала также оценивается до бесконечности. - person Kelson Ball; 25.07.2016

Int в Java является дополнением 2. Целое число с дополнением до двух не имеет доступных битов для хранения специальных значений, таких как Infinity или NaN, поэтому, поскольку результат не может быть представлен в желаемом типе, должно быть создано исключение. У чисел с плавающей запятой этой проблемы нет (для Infinity доступен битовый шаблон), и поэтому никаких исключений не требуется.

person codemonger    schedule 25.07.2016
comment
Этот вопрос не о java - person pquest; 25.07.2016
comment
Виноват! Хотя это относится и к C#. - person codemonger; 25.07.2016
comment
@codemonger С математической точки зрения N / 0 не равно бесконечности. Вызов исключения имеет больше смысла, чем возврат любого значения, даже если учесть, что числа с плавающей запятой поддерживают значение +/- бесконечность. NaN было бы более подходящим, чем бесконечность. - person Kelson Ball; 25.07.2016
comment
@KelsonBall NaN возвращается для 0/0. Во-первых, трудно определить 0 для чисел с плавающей запятой. - person Sergey.quixoticaxis.Ivanov; 01.06.2017