1. Обзор
Деление на ноль-это операция, которая не имеет смысла в обычной арифметике и, следовательно, не определена. Однако в программировании, хотя это часто связано с ошибкой, это не всегда так .
В этой статье мы рассмотрим что происходит, когда происходит деление на ноль в программе Java.
В соответствии со спецификацией Java операции деления, мы можем идентифицировать два различных случая деления на ноль: целые числа и числа с плавающей запятой.
2. Целые числа
Во-первых, для целых чисел все довольно просто. Деление целого числа на ноль приведет к исключению ArithmeticException :
assertThrows(ArithmeticException.class, () -> { int result = 12 / 0; });
assertThrows(ArithmeticException.class, () -> { int result = 0 / 0; });
3. Типы с Плавающей Запятой
Однако при работе с числами с плавающей запятой , исключение не будет выдано :
assertDoesNotThrow(() -> { float result = 12f / 0; });
Для обработки подобных случаев Java использует некоторые специальные числовые значения, которые могут представлять результаты такой операции: NaN , POSITIVE_INFINITY и NEGATIVE_INFINITY.
3.1. НаН
Давайте начнем с деления нулевых значений с плавающей запятой на ноль :
assertEquals(Float.NaN, 0f / 0); assertEquals(Double.NaN, 0d / 0);
Результатом в этих случаях является NaN (а не число).
3.2. Бесконечность
Далее, давайте разделим некоторые ненулевые значения на ноль :
assertEquals(Float.POSITIVE_INFINITY, 12f / 0); assertEquals(Double.POSITIVE_INFINITY, 12d / 0); assertEquals(Float.NEGATIVE_INFINITY, -12f / 0); assertEquals(Double.NEGATIVE_INFINITY, -12d / 0);
Как мы видим, результатом является БЕСКОНЕЧНОСТЬ, со знаком, зависящим от знака операндов.
Более того, мы также можем использовать понятие отрицательного нуля, чтобы добраться до NEGATIVE_INFINITY :
assertEquals(Float.NEGATIVE_INFINITY, 12f / -0f); assertEquals(Double.NEGATIVE_INFINITY, 12f / -0f);
3.3. Представление памяти
Итак, почему целочисленное деление на ноль вызывает исключение, а деление с плавающей запятой на ноль-нет?
Давайте посмотрим на это с точки зрения представления памяти. Для целых чисел нет битового шаблона, который можно использовать для хранения результата такой операции, в то время как числа с плавающей запятой имеют значения, такие как NaN или INFINITY , которые будут использоваться в подобных случаях.
Теперь давайте рассмотрим двоичное представление поплавка как S EEEEEEE E FFFFFFF FFFFFFFF FFFFFFFF с одним битом (S) для знака, 8 битами (E) для экспоненты и остальными (F) для мантиссы.
В каждом из трех значений NaN , POSITIVE_INFINITY, и NEGATIVE_INFINITY, все биты в части экспоненты имеют значение 1.
БЕСКОНЕЧНОСТЬ имеет все биты мантиссы равными 0, в то время как NaN имеет ненулевую мантиссу:
assertEquals(Float.POSITIVE_INFINITY, Float.intBitsToFloat(0b01111111100000000000000000000000)); assertEquals(Float.NEGATIVE_INFINITY, Float.intBitsToFloat(0b11111111100000000000000000000000)); assertEquals(Float.NaN, Float.intBitsToFloat(0b11111111100000010000000000000000)); assertEquals(Float.NaN, Float.intBitsToFloat(0b11111111100000011000000000100000));
4. Резюме
Подводя итог, в этой статье мы увидели, как работает деление на ноль в Java.
Такие значения, как INFINITY и NaN , доступны для чисел с плавающей запятой, но не для целых чисел . В результате деление целого числа на ноль приведет к исключению. Однако для float или double Java допускает эту операцию.
Полный код доступен на GitHub .