Страницы

Поиск по вопросам

вторник, 26 ноября 2019 г.

Знак ! в арифметических выражениях, зачем он?


Смотрел я как то, не так давно, почти что только что, код своего преподавателя, набирался уму - разуму, да попивал чай зеленый с коньячком, и вдруг встречаю я странную штуку:
k=k+!(k%2);

Углубился я в раздумья - "встречал то раньше я знак восхищения только в логически
выражениях, а тут вот на тебе"... И как бы я часто клавишу F10 не нажимал так понят
и не смог, зачем оно и что делает. Помню, как то, человек хороший, сказал мне мудрость вот такую, что напиши ты int a=0; a следом сразу a=~a; то а в мгновенья станет -1, так вот, возможноль, что и этот случай, который был описан выше, причастен к фокусам из этих серий?     


Ответы

Ответ 1



Да, это из той же серии, что и ~a. На самом деле !a эквивалентно (a == 0) и имеет тип bool. Но когда вы интерпретирует это значение как int (а у вас так и происходит, из-за сложения с целым), bool превращаетс в 0 или 1. Таким образом, как целое число наше выражение эквивалентно a == 0 ? 1 : 0. В частности, если известно, что a -- 0 или 1, то это просто меняет значение на противоположное: если был ноль, станет 1, иначе станет 0. Таким образом, выражение k+!(k%2) следует понимать так: если k было чётным, k%2 == 0, ! обращает значение в 1, и в результате получается k+1. если k было нечётным, k%2 != 0, ! обращает значение в 0, и в результате получается просто k. То есть результат -- ближайшее (сверху) нечётное число. Makes sense? Такая техника, как в вашем примере -- один из примеров кода, за который одни обожают а другие ненавидят язык С++ (и С). Специалисты, тяготеющие к С, обычно больше любя такую лаконичную запись, а те, кто видит в языке скорее высокоуровневые конструкции, чем биты и байты, часто морщатся от такого кода. Я отношу себя к любителям высокоуровневых языков, но мне такие лаконичные конструкции нравятся. Вот тут собрано много примеров подобного кода. Почитайте! Ещё несколько мозгоразрывающих примеров на SO: здесь и здесь. (Вынес из комментариев) Кстати, обратите внимание, что более коротким (и возможно более эффективным) кодом, выполняющим ту же задачу, является k |= 1; Он, кстати, работает и для отрицательных значений k (это не вполне очевидно).

Ответ 2



Знак ! обозначает отрицание. !0 равен 1, !все_остальное равно 0. Смысл k=k+!(k%2); Если k четное, оно увеличивается на единицу. Более короткая запись k|=1; Если разложить по шагам: X=k%2 if(X) X=0; else X=1; k=k+X;

Ответ 3



Вот @Rules, возможно, пытался сказать что-то более-менее правильное. Но до конц он так вам все и не объяснил. Дело в том, что логический тип в С++ - это алиас целочисленног типа, но, в отличие от целых чисел типа int, например, тип bool имеет целочисленные значения в диапазоне от [0;1]. Короче говоря, логический тип может принимать только два значения - 0(false), 1(true). Сказанное мною вы можете сами проверить, приведя "логику" к числам так, например: int a=(int)true; // 1 int b=(int)false // 0 И приводить, кстати говоря, явным образом не обязательно. Обратный пример: bool a=48555; // 1 bool b=0; // 0 bool b=-948; // 1( из этого следует, что false`ом может быть только нуль ) Из всего этого следует, что эта "конструкция": k=k+!(k%2); будет либо инкрементировать K, либо оставлять неизменным. Если (k%2)>0, то прибавляется нуль ( т.к !(true) - это false ), иначе - единица( т.к !(false) - это true). И никаких величин более единицы, как это вам показал @Rules ! P.S И вам, и пользователю @Rules было бы неплохо ознакомиться с булевой алгеброй.

Ответ 4



k=k+!(k%2); Значит: k равно k плюс не(остаток от деления k на 2); не(1) = 0; не(0) = 1; 5%2 = остаток от деления 5 на 2 = 1 (т.е (4/2)+1); 23%7 = остаток от деления 23 на 7 = 2 (т.е (21/7)+2); 24%2 = остаток от деления 24 на 2 = 0 (т.е (24/2)+0); итак: если k = 4 то k = 4 + !0 => k = 4 + 1 => k = 5; если k = 17 то k = 17 + !1 => k = 17 + 0 => k = 17 то есть k увеличивается на единицу если оно изначально делилось на 2 PS: забыл упомянуть: !(любое число кроме 0) = 0

Комментариев нет:

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