Что такое NaN в Java

🔥 🚀 Важно для всех, кто работает с 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».

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх