Перейти к основному содержимому

Деление на ноль в Java: исключение, бесконечность или не число

· 3 мин. чтения

Задача: Сумма двух чисел

Напишите функцию twoSum. Которая получает массив целых чисел nums и целую сумму target, а возвращает индексы двух чисел, сумма которых равна target. Любой набор входных данных имеет ровно одно решение, и вы не можете использовать один и тот же элемент дважды. Ответ можно возвращать в любом порядке...

ANDROMEDA

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. NaN

Начнем с деления нулевых значений с плавающей запятой на ноль :

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.

В INFINITY все биты мантиссы установлены на 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 .