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

Как рубин обрабатывает нулевое деление?

Я пытаюсь выяснить, как рубин обрабатывает нулевое деление. Ruby возвращает разные результаты, основанные на классе. Это то, что я пробовал

0/0     # => ZeroDivisionError: divided by 0    
1/0     # => ZeroDivisionError: divided by 0
1.0/0   # => Infinity
0.0/0.0 # => NaN

Что здесь происходит? Должен ли я получить ZeroDivisionError для всех вышеперечисленных случаев?

Обновление Является ли "Бесконечность" стандартным типом данных, то?

(1.0/0).class  # => Float
4b9b3361

Ответ 1

Ruby просто отслеживает стандарт IEEE 754 с плавающей точкой. Эта страница в Википедии неплохо объясняет, что вы видите. Многие современные языки используют тот же подход.

Интуитивно, поведение, которое вы видите, имеет прекрасный смысл. В общем,

1/<small number> = <big number>

Поэтому в пределе

1/0 -> Infinity    and similarly    -1/0 -> -Infinity

Infinity - постоянная, понимаемая подсистемой с плавающей запятой. С другой стороны,

0 / <any non-zero> = 0

Итак, у нас конфликт на 0/0. Должно ли оно быть нулем или бесконечностью? Стандартным ответом IEEE является "Не число", NaN, которое вы видите, другая константа с плавающей запятой.

Константы NaN и плюс или минус Infinity распространяются через выражения так же, как это имеет смысл. Например:

Infinity + <any (necessarly finite) number> = Infinity

и

<any number> + NaN = NaN

И более интересно:

1 / Infinity = 0

Что вы можете попробовать самостоятельно:

irb(main):005:0> 1.0 / (1.0 / 0.0)
=> 0.0

Таким образом, расчет с плавающей запятой может продолжаться даже тогда, когда он переполнен или делен на ноль и все же дает достаточно информативный ответ (хотя после того, как вы хорошо знаете стандартный уровень, вы увидите, что полагаться на ответ обычно плохо идея).

Это далеко не единственное поведение, которое предоставляет Стандарт. Другие могут быть выбраны. Но Ruby делает это за вас. Исходный файл numeric.c, функция Init_Numeric, устанавливает хост-процессор так, чтобы деление на ноль распространяло бесконечности. Другие языки могут делать другие варианты, например, для генерации исключения.

Ответ 2

Поведение для плавающих точек отражает стандарт в IEEE 754:

  • Неверная операция (например, квадратный корень из отрицательного числа) (возвращает qNaN).
  • Деление на ноль (операция с конечными операндами дает точный бесконечный результат, например, 1/0 или log (0)) (по умолчанию возвращает ± бесконечность).

Решение о внедрении ошибки времени выполнения для целочисленного деления на ноль является общим для многих других языков, таких как Java, С++, Python. Python также вызывает ошибку ZeroDivisionError.

См. ответ о причинах этих классификаций и поведения.

Ответ 3

Дело в том, что float имеет ошибку округления. Поплавок 0.0 не обязательно выражает точный нуль или 0.0 в математическом смысле, но отражает все числа, которые будут округлены до 0.0 с учетом точности. Разделение на точный 0 не определяется математически, но запрещение деления на 0.0 будет иметь опасность возврата ошибки в случае, если у делителя было ненулевое абсолютное значение, достаточно маленькое, чтобы округлить до 0.0. Вы не хотите, чтобы программа внезапно возвращала ошибку, когда абсолютное значение делителя мало, если оно не равно нулю. В случае поплавков безопаснее, чтобы система назначала определенное число на деление на 0.0, а не на запрет. Но это число не может быть выражено обычным способом, поэтому ему назначается NaN или Infinity. Что вводит в заблуждение об этом, так это то, что бесконечность не бесконечна в математическом смысле. Это просто означает "число больше любого другого числа, которое может быть выражено в этой системе". Это объясняет случай:

1.0/0.0 # => Infinity
0.0/0.0 # => NaN

Когда один аргумент / является поплавком, тип Ruby отбрасывает другого для float, поэтому

1/0.0
1.0/0

будет таким же, как 1.0/0.0.

С другой стороны, целое число 0 не содержит ошибки и точно равно нулю, поэтому такой опасности не существует. Имеет смысл поднять ошибку с нулевым делением. Это объясняет

1/0 # => Error
0/0 # => Error