• ,

Ответы на вопросы к собеседованию Level30

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 ÷ ±Infinity	                0
±Infinity × ±Infinity	        ±Infinity
±(не ноль) ÷ 0	                ±Infinity
Infinity + Infinity	        Infinity
±0 ÷ ±0	                        NaN
Infinity - Infinity	        NaN
±Infinity ÷ ±Infinity	        NaN
±Infinity × 0	                NaN


3 Как проверить, что в результате вычисления получилась бесконечность?
Есть ответ на StackOverFlow.
stackoverflow.com/questions/12952024/how-to-implement-infinity-in-java
Все сводится к выводу 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 Что такое ленивое вычисление выражения?
Статья: www.ibm.com/developerworks/ru/library/l-lazyprog/
Это ленивые вычисления (lazy evaluation). В ленивых вычислениях ни один параметр не вычисляется, пока в нем нет необходимости. Программы фактически начинаются с конца и работают от конца к началу. Программа вычисляет, что должно быть возвращено, и продолжает движение назад, чтобы определить, какое значение для этого требуется. В сущности каждая функция вызывается с promise'ами для каждого параметра. Когда для вычисления необходимо значение, тогда выполняется promise. Поскольку код выполняется только тогда, когда необходимо значение, это называется вызов по необходимости (call-by-need). В традиционных языках программирования вместо promise'ов передаются значения, это называется вызов по значению(call-by-value).
Технология программирования «вызов по необходимости» имеет ряд преимуществ. Потоки имплементируются автоматически. Ненужные значения никогда не вычисляются. Однако, поведение ленивых программ часто трудно предсказать. В программах типа «вызов по значению» порядок вычисления довольно предсказуем, поэтому любые time- или sequence-based вычисления относительно легко имплемнтировать. В ленивых языках, где специальные конструкции, например, monads, необходимы для описания явно упорядоченных событий, это намного труднее. Все это также делает связь с другими языками более трудной.
Существуют языки программирования, например, Haskell и Clean, использующие ленивое программирование по умолчанию. Кроме того, для некоторых языков, таких как Scheme, ML и другие, существуют ленивые версии.
Иногда, откладывая вычисления до тех пор, пока не понадобится их значение, вы можете оптимизировать скорость выполнения программы или реструктурировать программу в более понятную форму. Несмотря на свою ценность, методы ленивого программирования не слишком широко используются или даже не очень известны. Подумайте о том, чтобы добавить их в ваш арсенал.

10 Чем отличается использование && и & для типа boolean?
&& — это логическое «и». (В этом случае имеют место ленивые вычисления: некоторые вычисления опускаются, когда результат и так ясен)
& — это побитовое «и» (Если применить этот оператор к переменным типа Boolean, то ленивых вычислений происходить не будет)

6 комментариев

Joysi
  • Joysi
  • +1
Маленькое дополнение к 10-му)
Обычно логический оператор & применяют когда обязательно необходимо проверить все условия, например
if (b == 5 & str.equals("abc")) ...
выкинет NullPointerException при str=null и b=4, в отличии от
if (b == 5 && str.equals("abc")) ...

которое продолжит выполнять команды после оператора if.
DefNeo
Спасибо за ваше дополнение
Afli
  • Afli
  • 0
  • Комментарий отредактирован 2016-10-04 15:21:13 пользователем Afli
удалено
lichMax
1) Строку конвертируем в число, а в ней есть буквы. Ответ – NaN
2) Бесконечность минус бесконечность. Ответ — NaN
3) Многие другие ситуации, где в ответе ждут число, а получается неизвестно что.
Любая операция, где есть NaN, дает в результате NaN.
Это скорее относится к первому вопросу, чем ко второму.
lichMax
3 Как проверить, что в результате вычисления получилась бесконечность?

Не уверен в своём ответе, но по-моему можно просто сравнивать с Double.POSITIVE_INFINITY и Double.NEGATIVE_INFINITY.
anshelen
Можно использовать статические методы Double.isInfinite(double d) и Double.isFinite(double d). Также на случай чего есть метод Double.isNaN(double d).
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.