Несогласованность в поведении по принципу "один за другим" между разными типами значений
Пожалуйста, рассмотрите следующий код и комментарии:
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
РЕДАКТИРОВАТЬ:
Основываясь на отзывах Кекекела, я запустил следующее, что привело к бесконечности:
Console.WriteLine(1 / .0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001);
Этот тест, кажется, подтверждает идею, и буквальное двойное число 0.0
на самом деле очень, очень крошечная дробь, которая приведет к бесконечности...
Ответы
Ответ 1
В двух словах: тип double
определяет значение бесконечности, а тип int
- нет. Таким образом, в случае double
результат вычисления - это значение, которое вы действительно можете выразить в заданном типе, поскольку оно определено. В случае int
для бесконечности нет значения и, следовательно, нет способа вернуть точный результат. Отсюда исключение.
VB.NET делает вещи немного по-другому; целочисленное деление автоматически приводит к значению с плавающей запятой, используя оператор /
. Это позволяет разработчикам писать, например, выражение 1 / 2
, и оценивать его до 0.5
, что некоторые считают интуитивным. Если вы хотите увидеть поведение, совместимое с С#, попробуйте следующее:
Console.WriteLine(1 \ 0)
Обратите внимание на использование оператора целочисленного деления (\
, not /
) выше. Я считаю, что вы получите исключение (или ошибку компиляции - не знаете, какой).
Аналогично попробуйте следующее:
Dim x As Object = 1 / 0
Console.WriteLine(x.GetType())
Вышеприведенный код выводит System.Double
.
Что касается точки неточности, то здесь еще один способ взглянуть на нее. Дело не в том, что тип double
не имеет значения для ровно нуля (он делает); скорее, тип double
не предназначен для обеспечения математически точных результатов в первую очередь. (Определенные значения могут быть представлены точно, да, но расчеты не дают обещания точности.) В конце концов, значение математического выражения 1 / 0
не определено (последнее я проверил). Но 1 / x
стремится к бесконечности при приближении x к нулю. Итак, с этой точки зрения, если мы вообще не можем представлять большинство дробей n / m
, имеет смысл рассматривать случай x / 0
как приблизительный и давать значение, по которому оно приближается - опять же, бесконечность определяется, по крайней мере.
Ответ 2
Двойной номер числа с плавающей запятой, а не точное значение, поэтому то, что вы действительно разделяете с точки зрения компилятора, приближается к нулю, но не точно равно нулю.
Ответ 3
Потому что "числовая" плавающая точка не имеет ничего подобного. Операции с плавающей запятой:
- не являются ассоциативными
- не являются дистрибутивными
- может не иметь мультипликативного обратного
(см. http://www.cs.uiuc.edu/class/fa07/cs498mjg/notes/floating-point.pdf для некоторых примеров)
Плавающая точка - это конструкция для решения конкретной проблемы и используется повсюду, когда ее не должно быть. Я думаю, что они довольно ужасные, но это субъективно.
Ответ 4
Вероятно, это связано с тем, что стандартные числа с плавающей запятой с плавающей точкой и двойной точностью IEEE имеют заданное значение "бесконечность"..NET просто раскрывает то, что уже существует, на аппаратном уровне.
См. kekekela ответ, почему это имеет смысл, логически.
Ответ 5
Это по дизайну, потому что тип double
соответствует IEEE 754, стандарту для арифметики с плавающей запятой. Ознакомьтесь с документацией для Double.NegativeInfinity и Double.PositiveInfinity.
Значение этой константы является результатом деления положительного (или отрицательного) числа на ноль.