🔥 🚀 Важно для всех, кто работает с Java! 🔥
На JavaRocks ты найдешь уникальные туториалы, практические задачи и редкие книги, которых не найти в свободном доступе. Присоединяйся к нашему Telegram-каналу JavaRocks — стань частью профессионального сообщества!
В этой статье предлагаем узнать всё о NaN в Java. Вы можете выбрать удобный для вас формат — посмотреть видеоурок на английском языке, либо же прочитать текстовую версию ниже.
В Java термин “NaN” означает “Not a Number” (не число). Это не исключение, как можно было бы подумать. Более того, тип данных NaN является числовым. Начинающие программисты часто получают NaN случайно и продолжают использовать его в дальнейших вычислениях, что может привести к ошибкам. При попытке объединить несовместимые типы данных в Java может возникнуть ошибка времени выполнения (Throwable
).
Также можно столкнуться с мнением, что java.lang.ArithmeticException: / by zero
— это тот же NaN. Но Java обрабатывает их по-разному. Звучит запутанно? Ничего страшного, в этой статье мы подробно разберем, чем они отличаются.
Читайте также: 8 типичных ошибок начинающих Java-разработчиков
К концу прочитанного материала вы поймете, в чем отличия между NaN и арифметическими исключениями, при каких операциях может возникать NaN, и как его корректно обрабатывать.
Что такое NaN?
Итак, что же такое NaN? “NaN”, как многие из вас уже догадались, — это сокращение от Not a Number, что дословно означает “не число”. В Java это специальное значение для чисел с плавающей точкой, которое говорит об ошибке или невозможном результате. NaN появляется, например, при делении на ноль или если взять корень из отрицательного числа.
Вот небольшой пример:
public class NaN
{
public static void main(String[]args)
{
System.out.println(0.0 / 0.0); //zero divided by zero
System.out.println(Math.sqrt(-1)); //take sqrt of negative number
System.out.println(10.0 % 0); //taking mod by zero
}
}
Run CodeВывод в консоли:
NaN
NaN
NaN
В примере выше можно заметить, что NaN получается в результате трех простых операций:
- Деление нуля типа
float
илиdouble
на ноль. - Извлечение квадратного корня из отрицательного числа (
Math.sqrt(-x)
). В математике это дает мнимое число, а в Java — просто NaN. - Вычисление остатка от деления на ноль (
x % 0
). Java не может посчитать остаток в этом случае, поэтому возвращает NaN.
Чем NaN отличается от положительной и отрицательной бесконечности?
По стандарту IEEE 754 в Java есть три специальных значения для чисел с плавающей точкой (в том числе для double
), которые применяются в пограничных ситуациях:
- положительная бесконечность (
+Infinity
), - отрицательная бесконечность (
-Infinity
), - NaN (Not a Number).
Результатом деления положительного числа на 0 является положительная бесконечность. Аналогично, отрицательная бесконечность получается в результате деления отрицательного числа на ноль. Важно отметить, что все эти значения применимы как к float
, так и к double
. Однако float
обладает меньшей точностью, и в ряде случаев этого может быть недостаточно. Далее разберемся, как NaN ведет себя в обоих случаях — с float
и с double
.
Метод isNaN()
: что это и зачем нужен?
isNaN()
— это один из основных методов в Java, позволяющий проверить, является ли значение NaN. Выше мы уже рассмотрели три случая, при которых может появиться NaN. Теперь посмотрим, как isNaN()
позволяет отличить его от значений +infinity
и -infinity
.
public class isNaN
{ public static void main(String[]args)
{
Double posInfinity = +2.0 / 0.0;
Double negInfinity = -3.5 / 0.0;
Double nanVal = 50 % 0.0;
System.out.println ("+" + posInfinity + ".IsNaN() = " + posInfinity.isNaN());
System.out.println ( negInfinity + ".IsNaN() = " + negInfinity.isNaN());
System.out.println ( nanVal + ".IsNaN() = " + nanVal.isNaN());
}
}
Run CodeВывод в консоли:
+Infinity.IsNaN() = false
-Infinity.IsNaN() = false
NaN.IsNaN() = true
Как сравнивать значения NaN?
Каждое значение NaN считается отдельным, то есть NaN не равен даже самому себе. По этому принципу, если вы сравниваете одно значение с другим, результат всегда будет отрицательным. Поскольку NaN неупорядочен, любое числовое сравнение, в котором участвует хотя бы один NaN, возвращает false
. Java предоставляет две константы — Float.NaN
и Double.NaN
, — которые используются для работы с NaN. Поведение при сравнении может быть следующим:
true
— только в случае неравенства (!=)false
— для всех остальных операций сравнения (==, <=, >=, <, >)
Вот пример:
public class ComparingNaN
{ public static void main(String[] args)
{
// Comparing NaN values for Float constants
System.out.println (Float.NaN != Float.NaN); // true
System.out.println (Float.NaN == Float.NaN); // false
System.out.println (Float.NaN < Float.NaN); // false
System.out.println (Float.NaN > Float.NaN); // false
System.out.println (Float.NaN <= Float.NaN); // false
System.out.println (Float.NaN >= Float.NaN); // false
// Comparing NaN values for Float constants
System.out.println (Double.NaN != Double.NaN); // true
System.out.println (Double.NaN == Double.NaN); // false
System.out.println (Double.NaN < Double.NaN); // false
System.out.println (Double.NaN > Double.NaN); // false
System.out.println (Double.NaN <= Double.NaN); // false
System.out.println (Double.NaN >= Double.NaN); // false
}
}
Run CodeКак сгенерировать значение NaN?
Прежде чем подводить итоги, давайте рассмотрим несколько типичных ситуаций, в которых можно получить значение NaN (не число).
public class GenerateNaNValues {
static final float ZERO = 0;
public static void main (String[]args)
{
System.out.println("ZERO / ZERO = " + (ZERO / ZERO));
System.out.println("+INFINITY - INFINITY = " +
(Float.POSITIVE_INFINITY + Float.NEGATIVE_INFINITY));
System.out.println("-INFINITY * ZERO = " + (Float.NEGATIVE_INFINITY * ZERO));
System.out.println("+INFINITY * ZERO = " + (Float.POSITIVE_INFINITY * ZERO));
System.out.println("log10(-10) = " + Math.log(-10));
System.out.println("√-10 = " + Math.sqrt(-10));
System.out.println("NaN + 10 = " + (Float.NaN + 10));
System.out.println("NaN - 10 = " + (Float.NaN - 10));
System.out.println("NaN * 10 = " + (Float.NaN * 10));
System.out.println("NaN / 10 = " + (Float.NaN / 10));
System.out.println("NaN + NaN = " + (Float.NaN + Float.NaN));
System.out.println("NaN - NaN = " + (Float.NaN - Float.NaN));
System.out.println("NaN * NaN = " + (Float.NaN * Float.NaN));
System.out.println("NaN / NaN = " + (Float.NaN / Float.NaN));
}
}
Run CodeВывод в консоли:
ZERO / ZERO = NaN
+INFINITY - INFINITY = NaN
-INFINITY * ZERO = NaN
+INFINITY * ZERO = NaN
log10(-10) = NaN
√-10 = NaN
NaN + 10 = NaN
NaN - 10 = NaN
NaN * 10 = NaN
NaN / 10 = NaN
NaN + NaN = NaN
NaN - NaN = NaN
NaN * NaN = NaN
NaN / NaN = NaN
Заключение
После того как мы рассмотрели разные примеры появления NaN, вы наверняка лучше понимаете, как он работает. Сначала он может сбивать с толку, но на самом деле ничего сложного в этом нет. Проверка значений на NaN в вычислениях с плавающей точкой — полезная привычка, которая избавит от множества проблем.
Если вы забудете это правило — ничего страшного. Вы всегда можете вернуться к этой статье. Написание компилируемого и безошибочного кода с первого раза — это результат опыта. Так что не бойтесь писать код и создавать классные проекты!
Перевод статьи «NaN in Java».