Несоответствие в поведении деления на ноль между различными типами значений - PullRequest
40 голосов
/ 06 января 2011

Пожалуйста, обратите внимание на следующий код и комментарии:

Console.WriteLine(1 / 0); // will not compile, error: Division by constant zero

int i = 0;
Console.WriteLine(1 / i); // compiles, runs, throws: DivideByZeroException

double d = 0;
Console.WriteLine(1 / d); // compiles, runs, results in: Infinity   

Я могу понять, что компилятор активно проверяет деление на нулевую константу и DivideByZeroException во время выполнения, но:

Почему использование двойного числа в делении на ноль возвращает бесконечность, а не генерирует исключение? Это по замыслу или это ошибка?

Просто ради удовольствия, я сделал это и в VB.NET, с "более последовательными" результатами:

dim d as double = 0.0
Console.WriteLine(1 / d) ' compiles, runs, results in: Infinity

dim i as Integer = 0
Console.WriteLine(1 / i) '  compiles, runs, results in: Infinity

Console.WriteLine(1 / 0) ' compiles, runs, results in: Infinity

EDIT:

Основываясь на отзывах Кекекелы, я запустил следующее, что привело к бесконечности:

Console.WriteLine(1 / .0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001);

Этот тест, кажется, подтверждает идею, и буквальное двойное число 0.0 на самом деле очень, очень крошечная дробь, которая приведет к бесконечности ...

Ответы [ 5 ]

35 голосов
/ 06 января 2011

В двух словах: тип double определяет значение для бесконечности, а тип int - нет.Так что в случае double результатом вычисления является значение, которое вы можете фактически выразить в данном типе, поскольку он определен.В случае int значение бесконечности отсутствует и, следовательно, нет способа вернуть точный результат.Отсюда и исключение.

VB.NET работает немного иначе;целочисленное деление автоматически приводит к значению с плавающей запятой, используя оператор /.Это позволяет разработчикам писать, например, выражение 1 / 2 и оценивать его как 0.5, что некоторые считают интуитивно понятным.Если вы хотите, чтобы поведение соответствовало C #, попробуйте следующее:

Console.WriteLine(1 \ 0)

Обратите внимание на использование оператора целочисленного деления (\, а не /) выше.Я полагаю, вы получите исключение (или ошибку компиляции - не знаете, какая именно).

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

Dim x As Object = 1 / 0
Console.WriteLine(x.GetType())

Приведенный выше код выведет System.Double.

Что касается вопроса о неточности, вот другой способ взглянуть на это.Дело не в том, что тип double не имеет значения точно для нуля (это так);скорее тип double не предназначен для предоставления математически точных результатов в первую очередь.(Определенные значения могут быть представлены точно, да. Но вычисления не дают никакой гарантии точности.) В конце концов, значение математического выражения 1 / 0 не определено (последний раз я проверял).Но 1 / x приближается к бесконечности, так как x приближается к нулю.Таким образом, с этой точки зрения, если мы не можем представить большинство фракций n / m точно в любом случае, имеет смысл рассматривать случай x / 0 как приблизительный и задавать значение, которое приближается к - снова,бесконечность определяется, как минимум.

7 голосов
/ 06 января 2011

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

2 голосов
/ 19 марта 2013

Потому что «числовая» плавающая точка не имеет ничего общего. Операции с плавающей точкой:

  • не ассоциативны
  • не являются дистрибутивными
  • не может иметь мультипликативное обратное

(см. http://www.cs.uiuc.edu/class/fa07/cs498mjg/notes/floating-point.pdf для некоторых примеров)

Плавающая точка - это конструкция для решения конкретной проблемы, и она используется повсюду, когда этого не должно быть Я думаю, что они довольно ужасны, но это субъективно.

1 голос
/ 06 января 2011

Это сделано специально, потому что тип double соответствует IEEE 754 , стандарту для арифметики с плавающей точкой.Ознакомьтесь с документацией для Double.NegativeInfinity и Double.PositiveInfinity .

Значение этой константы является результатом деления положительного {или отрицательного}число на ноль.

1 голос
/ 06 января 2011

Скорее всего, это связано с тем, что стандартные числа с плавающей запятой IEEE и числа с плавающей запятой двойной точности имеют заданное значение "бесконечность". .NET просто показывает то, что уже существует, на аппаратном уровне.

См. Ответ Кекекела, почему это логично, логично.

...