JavaRush /Java блог /Архив info.javarush /Уровень 30. Ответы на вопросы к собеседованию по теме уро...
DefNeo
36 уровень

Уровень 30. Ответы на вопросы к собеседованию по теме уровня

Статья из группы Архив info.javarush
Уровень 30. Ответы на вопросы к собеседованию по теме уровня - 1
  1. Что такое NaN?

    NaN (англ. Not-a-Number) — одно из особых состояний числа с плавающей запятой. Используется во многих математических библиотеках и математических сопроцессорах. Данное состояние может возникнуть в различных случаях, например, когда предыдущая математическая операция завершилась с неопределённым результатом, или если в ячейку памяти попало не удовлетворяющее условиям число.

    В соответствии с IEEE 754, такое состояние задаётся через установку показателя степени в зарезервированное значение 11…11, а мантиссы — во что угодно, кроме 0 (зарезервированное значение для машинной бесконечности). Знак и мантисса могут нести какую-то дополнительную информацию: многие библиотеки «отрицательный» NaN выводят как -NaN.

    К операциям, приводящим к появлению NaN в качестве ответа, относятся:

    • все математические операции, содержащие NaN в качестве одного из операндов;
    • деление нуля на нуль;
    • деление бесконечности на бесконечность;
    • умножение нуля на бесконечность;
    • сложение бесконечности с бесконечностью противоположного знака;
    • вычисление квадратного корня отрицательного числа[1];
    • логарифмирование отрицательного числа.

    В некоторых языках программирования есть «тихий» и «сигнальный» NaN: первый, попав в любую операцию, возвращает NaN, второй — вызывает аварийную ситуацию. Обычно «тихий» или «сигнальный» определяется старшим битом мантиссы.

    NaN не равен ни одному другому значению (даже самому себе[2]); соответственно, самый простой метод проверки результата на NaN — это сравнение полученной величины с самой собой.

    Поведение других операций сравнения зависит от языка. Одни языки дают ложь[3] (так что a < b и b > a по-разному ведут себя с NaN), другие — выбрасывают аварию даже для «тихого» NaN.

    Любая нетривиальная операция, принимающая «тихий» NaN как аргумент, всегда возвращает NaN вне зависимости от значения других аргументов. Единственными исключениями из этого правила являются функции max и min, которые возвращают значение «второго» аргумента (отличного от NaN). Тривиальные операции, являющиеся тождеством, обрабатываются особо: так, например, 1NaN равно 1.

  2. Как получить бесконечность в Java?

    В Java тип double имеет специальные значения для понятий «плюс бесконечность» и «минус бесконечность». Положительное число, разделенное на 0.0, дает «плюс бесконечность», а отрицательное – «минус бесконечность». Этим понятиям соответствуют специальные константы типа Double:

    КодОписание
    public static final double POSITIVE_INFINITY = 1.0 / 0.0;плюс бесконечность
    public static final double NEGATIVE_INFINITY = -1.0 / 0.0;минус бесконечность
    1. Строку конвертируем в число, а в ней есть буквы. Ответ — NaN
    2. Бесконечность минус бесконечность. Ответ — NaN
    3. Многие другие ситуации, где в ответе ждут число, а получается неизвестно что.

    Любая операция, где есть NaN, дает в результате NaN.

    Действия с бесконечностью:
    ВыражениеРезультат
    n ÷ ±Infinity0
    ±Infinity × ±Infinity±Infinity
    ±(не ноль) ÷ ±Infinity
    Infinity + InfinityInfinity
    ±0 ÷ ±0NaN
    Infinity - InfinityNaN
    ±Infinity ÷ ±InfinityNaN
    ±Infinity × 0NaN
  3. Как проверить, что в результате вычисления получилась бесконечность?

    Есть ответ на StackOverFlow.

    Все сводится к выводу System.out.println()

  4. Что такое битовая маска?

    Битовая маска — это когда хранится много различных логических значений (true/false) в виде одного целого числа. При этом каждому boolean-значению соответствует определенный бит.

  5. Где применяют битовые маски?

    В основном там, где надо компактно хранить много информации об объектах. Когда хранишь много информации об объекте, всегда наберется пара десятков логических переменных. Вот их всех удобно хранить в одном числе. Именно хранить. Т.к. пользоваться им в работе не так уж удобно.

  6. Как установить бит в единицу в битовой маске?

    Опираясь на лекции можно ответить таким кодом:

    Здесь использовал метод Integer.toBinaryString(), дабы проверить себя, а вдруг)

    
    public class BitMask {
    
        public static void main(String[] args) {
            int a = 9;
    
           a |= (1<<2); // установить в 1 бит 2
    
    
            System.out.println(Integer.toBinaryString(a) + " "+ a);
        }
    }
    

    Вывод такой:

    1101 13

  7. Как установить бит в ноль в битовой маске?

    
    public class BitMask {
    
    public static void main(String[] args) {
    int a = 15;
    
    a &= ~(1<<2); // установить в 0 бит 2
    System.out.println(Integer.toBinaryString(a) + " "+ a);
    
        }
    }
    

    Вывод:

    1011 11

    Я взял число 15, так как на нем более наглядно видно, куда устанавливается 0.

  8. Как получить значение определенного бита в битовой маске?

    
    public class BitMask {
    
    public static void main(String[] args) {
         int a = 15;
    
         a &= ~(1<<2); // установить в 0 бит 2
    
         int c = a & (1<<2); // узнаем про 2 бит
         int d = a & (1<<3); // узнаем про 3 бит
        System.out.println(Integer.toBinaryString(a) + " "+ a + " " + c +" "+ d);
    
        }
    }
    

    Вывод:

    1011 11 0 8

    C 0 все понятно, на том месте и вправду 0. А переменная d возвращает значение запрашиваемого бита (в 10-ой системе).

  9. Что такое ленивое вычисление выражения?

    Статья: Ленивое программирование и ленивые вычисления

    Это ленивые вычисления (lazy evaluation). В ленивых вычислениях ни один параметр не вычисляется, пока в нем нет необходимости. Программы фактически начинаются с конца и работают от конца к началу. Программа вычисляет, что должно быть возвращено, и продолжает движение назад, чтобы определить, какое значение для этого требуется. В сущности каждая функция вызывается с promise'ами для каждого параметра. Когда для вычисления необходимо значение, тогда выполняется promise. Поскольку код выполняется только тогда, когда необходимо значение, это называется вызов по необходимости (call-by-need). В традиционных языках программирования вместо promise'ов передаются значения, это называется вызов по значению(call-by-value).

    Технология программирования "вызов по необходимости" имеет ряд преимуществ. Потоки имплементируются автоматически. Ненужные значения никогда не вычисляются. Однако, поведение ленивых программ часто трудно предсказать. В программах типа "вызов по значению" порядок вычисления довольно предсказуем, поэтому любые time- или sequence-based вычисления относительно легко имплемнтировать. В ленивых языках, где специальные конструкции, например, monads, необходимы для описания явно упорядоченных событий, это намного труднее. Все это также делает связь с другими языками более трудной.

    Существуют языки программирования, например, Haskell и Clean, использующие ленивое программирование по умолчанию. Кроме того, для некоторых языков, таких как Scheme, ML и другие, существуют ленивые версии.

    Иногда, откладывая вычисления до тех пор, пока не понадобится их значение, вы можете оптимизировать скорость выполнения программы или реструктурировать программу в более понятную форму. Несмотря на свою ценность, методы ленивого программирования не слишком широко используются или даже не очень известны. Подумайте о том, чтобы добавить их в ваш арсенал.

  10. Чем отличается использование && и & для типа boolean?

    && — это логическое «и». (В этом случае имеют место ленивые вычисления: некоторые вычисления опускаются, когда результат и так ясен)

    & — это побитовое «и» (Если применить этот оператор к переменным типа Boolean, то ленивых вычислений происходить не будет)

Комментарии (19)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Pig Man Уровень 41
8 января 2021
Не туда впихнул про NaN

2. Как получить бесконечность в Java?
..
Строку конвертируем в число, а в ней есть буквы. Ответ — NaN
Бесконечность минус бесконечность. Ответ — NaN
Многие другие ситуации, где в ответе ждут число, а получается неизвестно что.
Любая операция, где есть NaN, дает в результате NaN.
Алексей Уровень 41
2 декабря 2020
В ответе на 2-й вопрос есть косяки: ±(не ноль) ÷ ±Infinity Здесь непонятно, что имелось ввиду ±0 ÷ ±0 NaN Здесь будет не NaN, а ArithmeticException
Kex Уровень 38 Expert
17 июня 2020
В моменте подумал что у меня крыша поехала, пока не прочел коменты) Очень сырая работа конечно!
Soros Уровень 39
21 марта 2020
А это что за бред в 9-м вопросе?! "...Программы фактически начинаются с конца и работают от конца к началу..." Ленивые вычисления (англ. lazy evaluation, также отложенные вычисления) — применяемая в некоторых языках программирования стратегия вычисления, согласно которой вычисления следует откладывать до тех пор, пока не понадобится их результат. Отложенные вычисления позволяют сократить общий объём вычислений за счёт тех вычислений, результаты которых не будут использованы. Логические выражения вычисляются слева направо и в них (в логических выражениях) работает принцип ленивых вычислений - вычислений только при необходимости. Если при вычислении части выражения ответ уже и так понятен, то остальная часть выражения не вычисляется.
Soros Уровень 39
20 марта 2020
Константы POSITIVE_INFINITY, NEGATIVE_INFINITY и NaN существуют в классах Double и Float. Для проверки соответствия этим значениям нельзя применять логические операторы. Чтобы проверить значения, нужно воспользоваться следующими статическими методами классов Float и Double:

isInfinite(floaf/double);
возвращает true, если значение равно плюс или минус бесконечности и false - в противном случае.

isFinite(float/double);
возвращает true, если значение не равно плюс или минус бесконечности или значению NaN, и false - в противном случае.

isNan(float/double);
возвращает true, если значение равно NaN, и false - в противном случае. true/false - тип передаваемого в метод значения.
Artem Boyarshinov Уровень 35
7 декабря 2019
Что-то вы с ответами на 6, 7 и 8 перемудрили. 6. Как установить бит в единицу в битовой маске? Побитово сложить ее с маской, в которой все биты кроме нужного - нулевые:

byte b = 0b00000000;
b |= 0b00100000; //третий старший бит будет выставлен в 1
7. Как установить бит в ноль в битовой маске? Побитово умножить ее на маску, в которой все биты кроме нужного - единичные:

byte b = 0b01011010;
b &= 0b10111111; //второй старший бит будет выставлен в 0
8. Как получить значение определенного бита в битовой маске? Для того чтобы получить значение определенного бита в битовой маске необходимо умножить эту маску на другую маску, в которой все биты кроме интересующего имеют значение 0 и сравнить результат вычислений с нулем.

boolean b = c & 0b00010000 != 0; //Определяем выставлен ли в 1 пятый младший бит в переменной c
Даниил Уровень 41 Master
29 июня 2019
9-й вопрос перемудрили. Только начинают путать все эти утверждения об одном и том же только с разных сторон. Да и много лишней информации. Лучше просто пример привести, например как Joysi ниже привёл.
Даниил Уровень 41 Master
29 июня 2019
Вот сделал для себя на 6, 7 и 8 вопросы что бы нагляднее было

        //устанавливаем второй бит в "1"
        int a = 9;
        System.out.println("a : " + Integer.toBinaryString(a));
        a |= 1<<2;
        System.out.println("a |= 1<<2 : " + Integer.toBinaryString(a));

        //устанавливаем второй ит в "0"
        int b = 15;
        System.out.println("b : " + Integer.toBinaryString(b));
        b &= ~(1<<2);
        System.out.println("b &= ~(1<<2) : " + Integer.toBinaryString(b));

        //получаем значение второго бита
        int c = a & (1<<2);
        c >>= 2;
        System.out.println("c : " + Integer.toBinaryString(c));
Вывод работы программы:

a : 1001
a |= 1<<2 : 1101
b : 1111
b &= ~(1<<2) : 1011
c : 1
Захар Уровень 32
5 января 2019

1) Строку конвертируем в число, а в ней есть буквы. Ответ – NaN
Это только меня одного смущает? Exception будет! NumberFormatException!
рустам Уровень 41
3 октября 2018
А вот я не согласен, что выражение a &= ~(1<<2) установит в 0 бит 2. Получится так: сначала мы получим 0000 0100, затем инвертируем и получим 1111 1011. А дальше начинается издевательство: так как первый бит единица, то число отрицательное, а значит это число хранится в дополнительном коде. И оно преобразуется, опять инвертируется и добавляется 1: 0000 0101...или я где-то не прав?