Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Детская ошибка
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
Страницы: 1, 2, 3, 4
GetSmart
Цитата(halfdoom @ May 18 2016, 20:15) *
Код
e = a ^ 0xffff;
f = b ^ 0xffff;

Будет ли c == e и d == f?
Причем заметьте, что две последние строки, это точная побитовая инверсия переменных a и b.

Ошибаетесь. Точная "побитовая инверсия переменных a и b" это a^=0xffff и b^=0xffff. Другой вариант того же самого действия a=~a и b=~b

Операторы же выполняются строго после расширения (узких) rvalue не менее чем до int. Все побитовые логические и арифметические операторы. Все они работают со значениями ака rvalue. И у тильды ничего отличного от других нет, чтобы считать её особенной. Кроме того, что она унарная. Из-за чего могут быть два расширения по чужим, а не её правилам. Всё остальное в этих примерах очевидно
aiwa
Цитата(GetSmart @ May 18 2016, 19:13) *
В десятый раз: тильда не определена, а названа. Неудачно. Т.к. по факту действий точнее другое название. И у тильды нет своего особого расширения. Опять всё мимо.

Вот это уже действительно тавтология. Название - это графа "имя" в паспорте или надпись на дорожном знаке.
А вот в стандарте для тильды есть жесткое определение, я приводил его раньше, повторюсь:
Цитата
5.3.1.10 The operand of ~ shall have integral or unscoped enumeration type; the result is the ones’ complement of its operand.
Integral promotions are performed. The type of the result is the type of the promoted operand.

Это является определением, потому что:
1. названа область определения оператора (целые типы и эквивалентные им перечисления)
2. названа аналитическая формула оператора, правда косвенно, но через определение дополнения до 2-х.

Более того, в стандарте особо подчеркивается, что это дополнение но не порязрядное отрицание, хотя оба имеют идентичный результат.
посмотрите альтернативную форму записи операторов:
написание (a & b ) идентично написанию ( а bitand b ), соответственно (a | b ) эквивалентно (a bitor b ).
Но bitnot отсутствует: для тильды предусмотрен буквенный эквивалент compl - сокращение от дополнения.
juvf
Цитата(amiller @ May 2 2016, 13:21) *
1. Раньше во всех случаях, когда в коде был фрагмент, который никогда не выполняется, я получал от компилятора соответствующее предупреждение. В данном случае - нет.


Цитата(amiller @ May 2 2016, 16:35) *
В этом с Вами я согласен, но предупредить то надо. Например если поставить внутри функции безусловный return, генерится предупреждение. А в данном случае всё происходит молча. Ну и не совсем понятен алгоритм. В одном случае молча выбрасывает код, а в другом очень похожем случае всё же генерит код, который тем не менее также не выполняется.

Например, если в том же примере заменить строку с условным оператором "if (true) continue;" то получаем предупреждение:
Warning[Pe111]: statement is unreachable - что логично. А в моем примере это происходило "молча".

во 1-х.... не плохо бы указать какой язык, с или с++?
во 2-х.... мне кажеться ТС немного путает тёплое с мягким. Компилятор предупреждает о коде, который ни когда не выполнится, о недостижимых участках кода. Компилятор не предупреждает о том, что какой-то код не будет сгенерирован или будет сгенерирован.

Если вы напишите код....
Код
while(1);
printf("hello world");
то компилятор очевидно предупредит вас что printf не когда не выполниться. statement is unreachable - что логично. Он вас не предупредить о том, что он что-то там будет или не будет генерить. Он вас предупредит о недостижимых участках кода.
Если компилятор встретит 100 строк кода, типа
Код
int16u temp;
temp = 0x0001;
temp = 0x0002;
temp = 0x0003;
temp = 0x0004;
...
temp = 100;
То, шагая по строчкам си-кода, программа выполнит все 100 строчек. Формально, каждая строка в этом коде достижима и выполнима. Результат такого кода очевиден temp = 100, и как бы программа не работала, какая бы оптимизация не стояла, после выполнения всех строк будет temp = 100. Поэтому исполнитель вправе выполнить только 1 строчку temp = 100. И компилятор вправе/может выкинуть 99 строк. Даже не выкинуть, а считайте так: компилятор из 100 строк си-кода сгенерировал 1 машинную команду. Но то, что он из 100 строк си-кода сделал 1 строку асма - об этом он вам не скажет.
amiller
1. В данном проекте и данном примере плюсы не использовались, версию компилятора IAR уже приводил в начале. Думаю, что это роли не играет, более важным является уровень оптимизации - none.
2. Вы как то слишком уж своеобразно привели примеры для демонстрации своих слов:
Код
while(1);
printf("hello world");
Действительно warning будет.
Код
while(temp1 != ~temp2);
printf("hello world");
Аналогичного предупреждения не будет, хотя компилятор настолько уверен в ненужности этого кода, что спокойно его выбрасывает, включая while.
Примерно к 50 сообщению мы уже разобрались, что если включить remarks, то сообщения компилятора будут, хотя и несколько другие.
Думаю, что уже нет никакого смысла обсуждать конкретную проблему, послужившую поводом для этой дискуссии.
Увлекательное обсуждение уже давно сместилось даже не в область обсуждения действий компилятора, а в область обсуждения стандарта языка Си.
Хотя количество сообщений косвенно говорит о том, что и поведение компилятора вызвало много вопросов и в консерватории (стандарте) не всё гладко.
GetSmart
Цитата(aiwa @ May 18 2016, 22:03) *
Это является определением, потому что:

Даже не знаю что добавить к ранее сказанному, кроме

Ой, всё!

И хочется верить, что на той стороне было несколько подмигиваний.

Тильдатеплонагреватель

Выбор варианта названия и определения был неудачный. А когда стало очевидно, что этот оператор ничего кроме побитовой инверсии "не сможет", то признать это стандарт до сих пор не смог. Для ясности терминологии. Но с ясностью там проблемы во многих местах.
aiwa
Цитата(GetSmart @ May 19 2016, 12:29) *
И хочется верить, что на той стороне было несколько подмигиваний.

Подмигивания действительно были, но не мои: комбинация cool.gif автоматически менялась на смайлик.
Пришлось пару раз редактировать пробелом.

С добавлением к ранее сказанному полностью согласен - уже все сказано, тем более тема начала вызывать раздражение.

З.Ы. Если анонсированная multilevel phase-change memory разовьется и в блоках управления процессора, то тема как феникс возродится.


GetSmart
Цитата(aiwa @ May 19 2016, 15:08) *
З.Ы. Если анонсированная multilevel phase-change memory разовьется и в блоках управления процессора, то тема как феникс возродится.

Она не взлетит. Другие правила обрезали ей крылья. В сочетании с ними определение стало некорректно. Если выражение "the result is the ones’ complement of its operand." подразумевает что-то большее, чем побитовую инверсию операнда, как тут некоторые утверждали. Название термина "дополнение до единицы" относительно однозначно только в однобитовом двоичном толковании (булевый - частный случай). В другом толковании обязано присутствовать определение (действий) термина. Если этого определения применительно к целочисленным операндам в стандарте нет и нет однозначности в том, откуда эта терминология наследуется, то применение такой терминологии некорректно.

По правилам Си (как минимум вне препроцессора) все целочисленные/enum rvalue - двоичные. Могут быть и со знаком и без. Ширина операнда (в т.ч. числовых констант) и до и после применения тильды ограничена и следить за ней должен программист. Эти обстоятельства могут дополнять и обрезать наследуемую терминологию "ones’ complement integral operand", но вот чью именно.

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

Цитата(aiwa)
В силу того, что побитовые логические операции не имеют никакого отношения к знаковости переменной и они не имеют совершенно никакого отношения к арифметике.

Никогда не видели выражений вроде x = y & -4 ? Или & -4U. Знаковое расширение вместе с неявным пред-расширением в int бывает удобно и при применении тильды.

Цитата(aiwa)
А вот унарный оператор ~ стоит особняком: его операнд самодостаточен и совершенно не требует проведения расширения.
Но в отличие от бинарных операций результат работы ~ нельзя скорректировать явным преобразованием ДО выполнения операций - только лишь после.

Что нельзя? Наоборот, для мультиплатформенности очень желательно перед тильдой ставить явное преобразование к не меньшему, чем размер самого широкого из ожидаемых int. Например .. ~(uint32_t)(expression) .. Ширину операнда выполнение тильды не меняет (учитывая авто-пред-расширение). Соответственно, если перед тильдой операнд явно расширить до любой нужной ширины, не менее int, то это именно то самое.

На счёт самодостаточности и не требования расширения - чуть ниже.
Цитата(aiwa)
Но если ему предстоит наливать вино (побитовые операции),
то лучше бы чтобы размерность тары совпадала с объемом вина.
Сегодняшний стандарт настаивает чтобы вино было налито в тазик - согласно расширению.

В таких правилах тоже тесть проблема. Если я правильно понял.

u16 a;
u32 b,c;
c = b & ~a;

Если бы (в альтернативном Си) тильде не требовалось расширение, то ~a осталось бы 16-битной, а в "c" отсутствовали бы старшие 16 бит из "b". Ну а если & или | или ^ нельзя будет применять к разным по ширине операндам, то это крайне неудобно из-за лишнего текста с явными приведениями.

Вспомнилась ещё одна необсуждённая сторона тильды. Применение тильды к нетипизированной целочисленной константе. Особенно в сочетании с несколькими, в т.ч. арифметическими операциями на этапе компиляции. Особенно в препроцессоре (#if). Может быть терминология в эту сторону смотрела.

Если "оборудование этого кинотеатра" (ака стандарта) показывает только 2D-фильмы, то называть в стандарте что-то 3D-терминами не совсем корректно, при существовании более точной 2D-терминологии. Или без корректировки 3D-термина в ближайшем контексте. А противопоставлять 3D-термин более точному 2D-термину совсем некорректно. Некорректно так же, если 3D-термин скрывает или вводит неоднозначность в важные свойства 2D-термина. При наличии 2D-термина не имеющего некорректностей. 3D в данном случае образ терминологии из более широкой базы (математики, языков программитрования и пр.).
GetSmart
Цитата
Это пункт 5. Unary operators.
Формально тильда не относится к группе логических поразрядных операторов.

Это утверждение основано только на определении тильды? Или откуда-то ещё следует, что не относится? В двоичном дополнительном коде, а это однозначная база вычислений с целыми числами в Си и его препроцессоре, и в конечной и в бесконечной сетке/ширине операнда действие, описанное как ones’ complement, которое однозначно понимается при инверсии одиночных бит, у целых чисел (применяясь ко множеству бит) инвертирующее двоичные биты в якобы частном случае своего функционала, ничего другого просто не сможет. В таких обстоятельствах действие строго равно поразрядной логической инверсии. Утверждение, что здесь (ones’ complement) формально не равно - ложно. Если это утверждение только что тильда не в группе, то чуть ниже. Насколько название ones’ complement применяемое к целым числам корректно - это другой вопрос.

Некорректность, повторюсь, в возможном отсутствии определения действия ones’ complement на целочисленные операнды Си. Ещё в возможном противопоставлении более точному действию/определению. И возможном невхождении в группу логических побитовых операторов, т.к. ничего другого тильда делать не может. А если бы что-то и могла, зависило от конфликта с группой. Название группы даже с оговоренным списком обязывает включать в неё все элементы подходящие к названию. При этом утверждение, что что-то не входит - корректно, только если оно описано как исключение. Часто в преамбулах каких-то разделов законодательства описывают ключевые признаки понятий, границ, целей. и прочего Ключевые признаки (чего-угодно) вполне допускают исключения, описанные где-то рядом с указанием причин. Даже когда в законе прямо не определены ключевые признаки, то они могут выделяться из элементов групп и прочих взаимосвязей, сверять логическую корректность закона и экстраполировать его. Приоритеты аналогично. Это к неописанным в стандарте вещах, вроде обсуждаемых ранее сообщений компилятора.
aiwa
Цитата(GetSmart @ May 19 2016, 15:53) *
Если выражение "the result is the ones’ complement of its operand." подразумевает что-то большее, чем побитовую инверсию операнда, как тут некоторые утверждали.

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


Цитата(GetSmart @ May 19 2016, 15:53) *
В другом толковании обязано присутствовать определение (действий) термина. Если этого определения применительно к целочисленным операндам в стандарте нет и нет однозначности в том, откуда эта терминология наследуется, то применение такой терминологии некорректно.

Вы невнимательны, я определение действий уже приводил
(n4582) 5.3.1.8.
The negative of an unsigned quantity is computed by subtracting its value from 2n, where n is the number of bits in the promoted operand.
Действие тильды определено строго определено как вычитаение (естественно согласно правилам расширения).
Определение более корректно, в отличие от тех же побитовых операций, которые вводятся как результат действия одноименных функций.


Цитата(GetSmart @ May 19 2016, 15:53) *
По правилам Си (как минимум вне препроцессора) все целочисленные/enum rvalue - двоичные. Могут быть и со знаком и без. Ширина операнда (в т.ч. числовых констант) и до и после применения тильды ограничена и следить за ней должен программист. Эти обстоятельства могут дополнять и обрезать наследуемую терминологию "ones’ complement integral operand", но вот чью именно.
...........................
Что нельзя? Наоборот, для мультиплатформенности очень желательно перед тильдой ставить явное преобразование к не меньшему, чем размер самого широкого из ожидаемых int. Например .. ~(uint32_t)(expression) .. Ширину операнда выполнение тильды не меняет (учитывая авто-пред-расширение). Соответственно, если перед тильдой операнд явно расширить до любой нужной ширины, не менее int, то это именно то самое.
...........................

В несоответствии поведения компилятора стандарту и необходимости программисту отслеживать корректность своего кода сомнений ни разу не возникало.


Цитата(GetSmart @ May 19 2016, 15:53) *
Никогда не видели выражений вроде x = y & -4 ? Или & -4U. Знаковое расширение вместе с неявным пред-расширением в int бывает удобно и при применении тильды.

Всячески стараюсь избегать подобных конструкций.
Даже index[array] никогда не использую вместо array[index] несмотря на то, что это одно и тоже.
Предпочитаю иметь код максимально читабельным.


Цитата(GetSmart @ May 19 2016, 15:53) *
Вспомнилась ещё одна необсуждённая сторона тильды. Применение тильды к нетипизированной целочисленной константе. Особенно в сочетании с несколькими, в т.ч. арифметическими операциями на этапе компиляции. Особенно в препроцессоре (#if). Может быть терминология в эту сторону смотрела.

В случае константы действия, в принципе, те же: грубо говоря источником значения для расширенного операнда будет константа, а не значение переменной.



Цитата(GetSmart @ May 19 2016, 15:53) *
В таких правилах тоже тесть проблема. Если я правильно понял.

u16 a;
u32 b,c;
c = b & ~a;

Если бы (в альтернативном Си) тильде не требовалось расширение, то ~a осталось бы 16-битной, а в "c" отсутствовали бы старшие 16 бит из "b". Ну а если & или | или ^ нельзя будет применять к разным по ширине операндам, то это крайне неудобно из-за лишнего текста с явными приведениями.


С альтернативным Си, точнее С++, Вы правильно поняли: я не оспариваю как правильность действий компилятора в соответствии со стандартом, так и сам стандарт.
Я занимаюсь безнадежным делом: вербую сторонников альернативы. В которой тильда была бы побитовой операцией инверсии как набор элементарных логических not.
В которой операция n-разрядного операнда трактовалась бы как набор из n логических операций not.

Если не требовать расширения для тильды, то результат применения ~ к a действительно был бы 16-ти разрядным, но не операнд ~а.
Поменялся бы лишь порядок расширения:
Стандарт: a ->расширение до 32 бит -> набор 32-х "not"
Альтернатива: a -> набор 16-ти "not" -> расширение до 32 бит.

Т.е. в альтернативе c = b & ~a; был бы эквивалентен следующему: {u16 temp; temp=~a; c = b & temp; },
который никак не противоречит существующему стандарту.

Цитата(GetSmart @ May 19 2016, 15:53) *
3D в данном случае образ терминологии из более широкой базы (математики, языков программитрования и пр.).

Так оно и есть, стандарт не следит за точным и всеобъемлющим наличием точных формулировок: оно и понятно - у них и без этого работы выше крыши.
Вся терминология как раз позаимствована извне и в некоторой степени интуитивна.
Но если уж использовать аллегорию с кинотеатром, то логические операции - канал звука, запустили по каналу видео - арифметические операции.
И потом на основании того, что результативная картинка полностью совпадает с заранее согласно правилами обработки видеосигнала утверждается
о правильности выбора.
GetSmart
Цитата(aiwa)
Более того, в стандарте особо подчеркивается, что это дополнение но не порязрядное отрицание, хотя оба имеют идентичный результат.

Что именно "это"? Допустим ones’ complement.
Про тильду в английской цитате стандарта смысл такой, что её результат (ака результат её применения) равен чему-то, а не она есть это что-то. Вместе с зацитированным будет: в формате целых чисел Си результат тильды совпадает с поразрядной инверсией/отрицанием. Это полноценная логическая замена из вводных. И очень похоже на исходное определение. Вполне законно использовать корректные логические замены/выводы из вводных стандарта. И некорректно их отрицание. Достоверность утверждений используемых для замен, вроде зацитированного, должна предворительно проверяться. И вообще, право использовать обычные возможности обобщений естественного языка по наблюдаемым признакам не запретит даже кривая терминология. Особенно широкораспостранённые в компьютерной области.

В цитате выше не совсем ясно, о чём писал автор. Что ones’ complement это не поразрядное отрицание или что-то другое.

Цитата(aiwa @ May 20 2016, 19:58) *
Это выражение подразумевает не большее или меньшее побитовой инверсии, а совершенно другое: арифметическую операцию между 2 в наибольшей разрядной степени
и числом, к которому применяется операция дополнения. Это понятие из математического аппарата представления чисел в кодах и С++ его просто унаследовал.

Цитата исходника была бы более информативна. С++ тут не по делу. Из остального понятно только что действие определено только для ограниченных двоичных множеств. Какое арифметическое действие тоже не ясно. Но есть слабая догадка, что это всё не о тильде, а о унарном минусе.

Цитата(aiwa @ May 20 2016, 19:58) *
Вы невнимательны, я определение действий уже приводил
(n4582) 5.3.1.8.
The negative of an unsigned quantity is computed by subtracting its value from 2n, where n is the number of bits in the promoted operand.
Действие тильды определено строго определено как вычитаение (естественно согласно правилам расширения).
Определение более корректно, в отличие от тех же побитовых операций, которые вводятся как результат действия одноименных функций.

Видимо это исходник. Он об унарном минусе. Я внимателен. Обсуждать это пока не хочется. Т.к. это и не тильда и не ones’ complement. Определение которого пока не видно.

Цитата(aiwa @ May 20 2016, 19:58) *
В несоответствии поведения компилятора стандарту и необходимости программисту отслеживать корректность своего кода сомнений ни разу не возникало.

Тогда о чём это? (ниже)
Цитата
А вот унарный оператор ~ стоит особняком: его операнд самодостаточен и совершенно не требует проведения расширения.
Но в отличие от бинарных операций результат работы ~ нельзя скорректировать явным преобразованием ДО выполнения операций - только лишь после.


Цитата(aiwa)
Даже index[array] никогда не использую вместо array[index] несмотря на то, что это одно и тоже.

Ничего общего. Либо объясните. Если в правилах Си rvalue и со знаком и без автоматом расширяются и никаких ограничений смешивания логических побитовых и арифметических операторов в правилах нет, то и к числовой константе должны быть такие же требования. Ни грабли, ни нарушения какого-то общего правила не вижу. В отличие отцитаты.

Цитата(aiwa)
В случае константы действия, в принципе, те же: грубо говоря источником значения для расширенного операнда будет константа, а не значение переменной.

Там имелись ввиду ограничения в ширине. Как при задании константы (обнаружении при разборе текста), так и при дальнейшем вычислении выражения с константами. Несовпадение результата выражения в #if и аналогичного выражения из констант в коде было бы очень странным. Но я вставал пару раз в ИАРе на грабли, когда он -1UL в сравнениях if и в свиче компилировал с ошибкой. Вот тогда я не понял, что у него внутри делается, что возникает несоответствие. Причём тот же текст в более новых компиляторах компилировался 100% корректно. В свиче просто был case -1UL: , а в ифе сравнение какого-то u32_lvalue и -1UL. И если при явном ограничении (UL) констант возникают неожиданности, то от окончания U там наверное их ещё больше.
aiwa
Цитата(GetSmart @ May 20 2016, 19:43) *
Ага. Она самая. (цитата). Об унарном минусе. Я внимателен.

Да, виноват. Действительно, смотрел в разделе унарных операций, из-за зрения выхватил параграф с "-".
Тильда двумя параграфами ниже и описания алгоритма действительно нет.
Но в этом случае алгоритм следует самого определения оператора тильда как дополнения.
Хотя его легче получить как инверсию разрядов, но формально оно все же определяется как многочлен.
Сам алгоритм даже не важен - главное, что оператор арифметический.


Цитата(GetSmart @ May 20 2016, 19:43) *
И вообще, право использовать обычные возможности обобщений естественного языка (по наблюдаемым признакам) не запретит даже кривая терминология.

В принципе все возрат к 11 маю: halfdoom "Почему? Ведь это оператор дополнения до единицы, который по совместительству можно использовать для побитовой инверсии.".
И если все остальные поразрядные "вроде бы логические" операторы определены арифметическими насильно - из-за удобства оперировать по единой системе расширения
и преобразований целочисленных типов. То оператор ~ являлся арифметическим по определению как дополнения.



Цитата(GetSmart @ May 20 2016, 19:43) *
С++ тут не по делу.

Я, в принципе опирался на стандарт С++.
В стандарте Си тильда тоже арифметический оператор:
Цитата
The result of the ~ operator is the bitwise complement of its (promoted) operand (that is,
each bit in the result is set if and only if the corresponding bit in the converted operand is
not set).

т.е. арифметическое NOT и значится в "Unary arithmetic operators".



Цитата(GetSmart @ May 20 2016, 19:43) *
Тогда о чём это? (ниже)

После напоминания, что тильда - это не оператор побитовый логики not, а one's complement - уже ни о чем.
А до этого о том, что все арифметические операторы bitwise-группы использовались как, цитирую Вас, "обычные возможности обобщений естественного языка (по наблюдаемым признакам)", т.е. как набор операций булевой алгебры над разрядами, уничтожая при этом "арифметический" эффект реальных bitwise.

Цитата(GetSmart @ May 20 2016, 19:43) *
Там имелись ввиду ограничения в ширине. Как при задании/обнаружении константы, так и при дальнейшем вычислении выражения с константами. Но несовпадение результата выражения в #if и аналогичного выражения (из констант) в коде было бы очень странным.

Не вижу никакого противоречия. Разрядность констант точно такая же как и сейчас. Правила расширения те же.
GetSmart
У меня вчера инет сломался. Я исправил предыдущий свой пост, не читая поста за ним. Собирался ещё вчера, но получилось только сейчас.

Цитата
После напоминания, что тильда - это не оператор побитовый логики not, а one's complement - уже ни о чем.

Оператор тильду (~) даже называть так нельзя при отсутствии в стандарте определения этого (неочевидного к формату Си-чисел) действия. Без определения утверждение в этой цитате тоже некорректно.

Цитата
В стандарте Си тильда тоже арифметический оператор:

Почему арифметическое, если даже в скобках этого определения написано, что речь о множестве независимых (двоичных) бит? Хотя и рассмотрена только половина действия. Это ещё не криминал. Такое же определение scifi приводил в начале обсуждения. Про bitwise complement в общеупотребительном английском смысле ничего не могу сказать, что оно может обозначать. Его определения (для Си) не видел. Результат применения совпадающего по названию термина в общеупотребительной компьютерной области равен побитовой инверсии при двоичных одно и многобитовых аргументах. Никаких заёмов и переносов не предполагается. Действие логическое побитовое.

В тексте "each bit in the result is set if and only if the corresponding bit in the converted operand is not set" пара слов "converted operand" неоднозначно указывает на объект, на который должен "(promoted) operand". Но т.к. это было только в пояснении, а рядом вне пояснения "operator is the bitwise complement of its (promoted) operand" однозначно указывает, что действие происходит в (предворительно расширенном) операнде, то неоднозначности нет. И общие правила расширений операндов это подтверждают. Отсутствие в естественном языке, на которых даются определения, возможностей графов вызывает неоднозначности толкования.

Общение начинает напоминать троллинг.
aiwa
Цитата(GetSmart @ May 21 2016, 10:21) *
Общение начинает напоминать троллинг.

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

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

2. как операции алгебры двоичной арифметики. При этом "bitwise"-операции уже равноправны с остальными арифметическими операциями.

Практической разницы особой нет, но эстетически я приверженец первого варианта.
При котором бы ~var16 не переводился бы автоматически в ~((int32)var16).
Результат и правила расширения в стандарте как бы говорили о 2.-ом варианте, но расплывчатое определение действия как "AND function" (или "OR fuction") оставляли надежду на 1. вариант.

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

GetSmart
Цитата(aiwa)
Что касается самого стандарта, так в нем не написано что такое побитовые логические операции.
...
А до этого о том, что все арифметические операторы bitwise-группы использовались как, ...

Разве bitwise здесь значит не <побитовый>?? Допустим так и есть. bitwise-группа операторов есть, а побитовых логических операций нет? Огласите список bitwise-группы и ту их часть, которые арифметические. Речь везде шла о Си. Арифметические операции делаются с переносами или заёмами соседних бит. Побитовые операции могут делаться независимо в каждом бите. Общеизвестная группа побитовых операций, которая так же есть в большинстве процессоров: and, or, xor, not. Из них только not можно реализовать через арифметическое действие с двумя операндами. Хотя в логическом варианте действия операнд один. Ввиду одинаковости результата этих действий и предпочтений создателей стандарта, а так же подгонки под возможности/особенности какого-то исполнительного "железа" определение оператора выбирают создатели документа.Но приоритет терминологии и прочего исходно был за разработчиком или разработчиками языка, Если это право не передано стандартизаторам. По поводу терминологии Си++, как языке, основанном на Си, тоже возможны нюансы принадлежности прав в терминологии.

Всё-равно термины общеупотребительной компьютерной терминологии этот выбор не запрещает применять. Пока никто не смог указать, где не совпадает тильда и побитовая инверсия. В Си++ может где-то и не совпадает. Знаком только поверхностно. Но если в Си он определён как побитовый, то "тильда есть битовая инверсия" корректно в области Си. Утверждение обратного - ложно. Алгоритмы можно описывать/строить в общекомпьютерной терминологии, через побитовую инверсию. А в условиях Си и Си++ корректно утверждать, что это делается через тильду. Можно добавлять, что не только через тильду.

Цитата(aiwa)
правила расширения в стандарте как бы говорили о 2.-ом варианте,

Операция расширения - это просто согласование размерностей операндов. Можно придумать такой тип хранения целых чисел, удобный и для побитовых и для арифметических операций, в котором ширина операндов будет ограничена размером всей доступной памяти девайса, но не обязательно больше чем кол-во текущих информационных бит в числе и независимый от них ещё один бит. Который можно назвать "все невидимые биты". Применение побитовой инверсии к операнду такого типа будет инвертировать этот дополнительный бит и все остальные информационные биты. При необходимости увеличить ширину операнда для согласования с другим операндом, все новые биты заполняются значением этого дополнительного бита. Что такое расширение похоже на знаковое - совсем не важно. В таких числах удобно работать с полным набором логических побитовых операторов, в т.ч. инверсией. Результат выражения максимально похож на математическую формулу выражения, в которой все операнды должны авто-расширяться. В такой модели не обозначено ничего арифметического и, как его признак, знакового. Зачеркнул чтобы показать полноту модели в рамках побитовой логики. Бит, похожий на знаковый, и расширение похожее на знаковое не объявляет расширение арифметической операцией. Оба варианта расширений: беззнаковое и похожее на знаковое. При желании и некоторой осторожности в Си можно использовать такое же удобство согласования ширины побитовых операндов. Старший бит переменных со знаковым типом резервировать для "невидимых бит". Логические побитовые операторы &, |, ^ будут работать с этими числами строго как раньше, одинаково со всеми битами (расширенного) rvalue. И даже двухстадийное расширение операнда для тильды не содержит граблю. Нужно только чтобы все переменные, которые будут участвовать в логических выражениях были объявлены со знаком и в знаковый бит не попадали информационные биты. При использовании констант в выражениях они тоже должны быть в знаковом формате (без U в окончании или unsigned type-cast)., если по правилам расширения из-за этой константы результат становится беззнаковым. Не знаю, насколько это баллов по эстетическим меркам, но эта модель демонстрирует приоритет/удобство использования тильды для действия побитовой инверсии.

В ранее приводимых мной примерах логической операции с отрицательной константой (& -4), её отрицательным значением задавались как раз некие невидимые старшие биты маски при укорочении текстового представления константы.

Вобщем-то обсуждение бессмысленно, если даже в стандарте Си тильда определена как побитовая.
aiwa
Цитата(GetSmart @ May 22 2016, 03:45) *
Арифметические операции делаются с переносами или заёмами соседних бит. Побитовые операции могут делаться независимо в каждом бите.

Термин арифметические означает, что bitwise операции определены в контексте двоичной арифметики многочленов, на которой базируется кодирование чисел и, соответственно, операции над ними.
Цитата(GetSmart @ May 22 2016, 03:45) *
Из них только not можно реализовать через арифметическое действие с двумя операндами. Хотя в логическом варианте действия операнд один.
..................
По поводу терминологии Си++, как языке, основанном на Си, тоже возможны нюансы принадлежности прав в терминологии.

Извините за несерьезные ссылки на википедию: я хочу донести только идею, а так как не являюсь специалистом, нормальных ссылок под рукой нет.
bitwise operations
Там есть пункт "Mathematical equivalents", в котором и написаны настоящие определения операций.
Здесь приведены математические эквиваленты побитовых операций, то есть их строгое определение в контексте двоичных многочленов.
Они определены как "bitwise OR", "bitwise AND" и т.д.
Поэтому не важно, что означает перевод "bitwise" - он не определяет операцию, он нам напоминает о ней.
Но если уж быть точным, то аргументом тильды как "bitwise NOT" является не набор битов, а двоичный многочлен кода числа.
Разработчики стандарта, ИМХО, корректно следовали именно этой терминологии.

Цитата(GetSmart @ May 22 2016, 03:45) *
Пока никто не смог указать, где не совпадает тильда и побитовая инверсия.

Несовпадение кроется в самой трактовке "побитовой инверсии".

Имеются два вида математических структур:

1. булевая алгебра, к которой большинство интуитивно относит "bitwise" операции.
Boolean algebra
Если рассматривать n-разрядный код числа как упорядоченный набор булевых переменных, то множество всех n-разрядных кодов образует булеву алгебру An, побитовая инверсия будет просто операцией not в этой алгебре.
Причем 16-ти разрядные операнды образуют алгебру А16, а 32-двухразрядные совершенно другую А32, а А16 будет подмножесвом А32.
В этом смысле операция (a16 & a32) бессмыслена по определению. Ее можно лишь трактовать как операцию с элементом из А32, эквивалентным элементу из А16.
Запись ~value16 интуитивно воспринимается как операция в А16, и соответственно результат этой операции как исключительно элемент А16.

2. двоичная арифметика многочленов (или как ее еще называют "по модулю 2")
Здесь операция "bitwise not" равноправно применима к кодам любой размерности и точно так же, как 0x01, 0x0001, 0x00000001 и. т.д. - это одно и тоже как число (1) в различном n-значном представлении, точно также и 0xFE, 0xFFFE, 0xFFFFFFFE по сути одно и то же - а именно операция ~ над числом 1.
Несмотря на "побитовое" различие между ними.

Грубо говоря, все недоразумения возникают потому, что большинство воспринимает побитовые операции в 1. смысле, то есть как операции над элементами булевой алгебры, а эти операции по стандарту являются операциями двоичной арифметики многочленов.
GetSmart
При преобразовании множества в многочлен теряется независимость. И простота логических операций. (Авто-)Расширение множества решается не созданием многочлена, а только наличием дополнительного бита у каждого операнда. Либо созданием правил расширения. Преобразование множества в многочлен этот дополнительный бит не порождает. А правила создания этого бита будут и в модели с множеством и в модели с многочленом. Во многих случаях принципиально иметь/работать с множеством, а не многочленом.
aiwa
Оказывается в С++11 структура "булевая алгебра" уже реализована посредством шаблона класса bitset.
С одной стороны получается даже излишнее: набор алгебр практически любой размерности, с другой стороны все "bitwise операции" в смысле буля реализованы только между членами класса.

Тогда топикстартовский пример становится корректным в смысле буля, если использовать явное преобразование.

~((bitset<16>)temp1) - инвертирование в смысле буля
~temp1 - ивертирование в смысле алгебры многочленов.

Остается дело за малым: bitset-ы размерности равных основным типам добавить к основным, при этом введя побитовые операции между типами bitset и основными, соответственно добавив функции а-ля "to_ulong" для этих размерностей - чтобы узаконить преобразования по умолчанию.
При этом, соответственно поделиться синонимами, так как это непозволительная роскошь попусту транжирить ключевые слова:
~, &, |, ^ - отдать основным битсетам, а comp, bitand, bitor, xor - оставить как есть - нынешним битвайзам.
Гуру языка, скорее всего, за такое изменение синтаксиса будет мучительно невыносимо, но зато начинающему большинству, еще не отягощенному знаниями стандарта такая реформа будет естественной.
GetSmart
Перечитываю себя и нахожу не совсем то, что хотел донести. От греха подальше поправлюсь.
Цитата(GetSmart)
(Авто-)Расширение множества решается не созданием многочлена, а только (в смысле только лишь) наличием дополнительного бита у каждого операнда.

(Авто-)Расширение множества решается, например, наличием дополнительного бита у каждого операнда.


Цитата(aiwa @ May 24 2016, 01:20) *
Оказывается в С++11 структура "булевая алгебра" уже реализована посредством шаблона класса bitset.

Огласите, пожалуйста, весь список где в С++ отличается результат тильды и побитовой инверсии. Если нет разницы, то зачем платить больше?
Применительно к целочисленным и enum операндам. Естественно, без переопределений тильды.
aiwa
Цитата(GetSmart @ May 24 2016, 22:31) *
Огласите, пожалуйста, весь список где в С++ отличается результат тильды и побитовой инверсии.

Список не предоставлю по причинам:
Во-первых я нигде не утверждал, что результат тильды в С++ отличается от побитовой инверсии.
Я только цитировал стандарт, что тильда определена как дополнение, что однозначно указывает принадлежность этой операции к набору операций двоичной арифметики многочленов.
Во вторых, также я не утверждал что компилятор компилирует не так как предписывает стандарт: компилятор строго ему следует.

Цитата(GetSmart @ May 24 2016, 22:31) *
Если нет разницы, то зачем платить больше?
Применительно к целочисленным и enum операндам. Естественно, без переопределений тильды.

Применительно к целочисленным и enum операндам, разница состоит в том, что приходится платить больше - набором дополнительных служебных слов и ухудшением из-за этого читабельности.
в обсуждаемом случае это для избавления от незванного подарка стандартом С++ в виде дополнительных поднятых 16-ти битах: (uint_16)(~var16)
Как другой, но более ресурсоемкий вариант: очистить результат маской, либо расширить вручную единичными разрядами.

В 98% случаев необходимость в побитовых операциях возникает не как к целочисленным (или enum) типам а именно как к набору бит.
98% я, конечно, написал от фонаря, 1% оставив разработчикам стандарта, другой 1% - прочитавшим стандарт для проверки, если вдруг захочется.
Косвенным доказательствам этого является введение в стандарт "по просьбам трудящихся" типа набора бит: bitset.

Появился третий вариант альтернативных действий, но тоже дополнительных: ~((bitset<16>)var16).

Но в 98% случаях нужно платить больше: набирать лишнее преобразование, захламляя текст, во втором - добавляя еще и команду, а в третьем вызывая еще и конструктор копирования.

При этом в оставшихся %2 случаях, текст кода остается "по умолчанию".

98% и 2% логично поменять местами.
vit496
Помогите пожалуйста восполнить пробел в познаниях языка.
почему компилятор может выбрасывать условие if(a==0)?

Код
void toggle_led ();

void main () {
  uint16_t i;
  uint8_t a;

  a = 5;
  for(i = 0; i < 500; i++) {
    a++;                                      
    if(a == 0) {
      toggle_led();  
    }
  }

  while(1);
}


ассемблер
Код
       NAME main
        PUBLIC main
        SECTION `.text`:CODE:NOROOT(1)
        THUMB
main:
??main_0:
        B.N      ??main_0

        END


Контроллер STM32F103, IAR ARM 6.10 (проверил и на 7.80), оптимизация по максимуму. toggle_led - внешняя функция, меняет состояние порта. Выбрасывается именно условие if(a==0), если поставить toggle_led без условия, то функция выполняется.
Шаманъ
Цитата(vit496 @ Apr 25 2017, 22:30) *
Помогите пожалуйста восполнить пробел в познаниях языка.
почему компилятор может выбрасывать условие if(a==0)?

Видимо оптимизатор компилятора не учитывает возможность переполнения, и для него переменная a принимает значения от 5 до 500.

Вообще Ваш код нечитабельный и потенциально опасный, ибо на первый взгляд неочевидно, что a может принимать нулевое значение. В более сложном коде подобный подход это источник багов.
k155la3
Цитата(vit496 @ Apr 25 2017, 22:30) *
Помогите пожалуйста восполнить пробел в познаниях языка.
почему компилятор может выбрасывать условие if(a==0)?


если Вам таки нужна переменная a вочтобытонистало

Код
__root  uint8_t a;

void main ()
{
  uint16_t i;
// uint8_t a;
  a = 5;
. . . .


Внимательно перечитайте все warn - запустите анализатор кода C-STAT StaticAnalysis (в меню Project).
Если оно есть в Вашем IAR/Target
Сергей Борщ
QUOTE (Шаманъ @ Apr 26 2017, 08:25) *
Видимо оптимизатор компилятора не учитывает возможность переполнения
Настолько диких отступлений от стандарта IAR позволять себе не должен.
QUOTE
6.2.5 Types
9 The range of nonnegative values of a signed integer type is a subrange of the
corresponding unsigned integer type, and the representation of the same value in each
type is the same. A computation involving unsigned operands can never overflow,
because a result that cannot be represented by the resulting unsigned integer type is
reduced modulo the number that is one greater than the largest value that can be
represented by the resulting type

Вот оставить два вызова toggle_led() и пустой цикл мог бы запросто.
GetSmart
Удалить if компилятор может как из-за условия, так и из-за тела. Условие обозначено вполне корректное. Даже volatile не требуется переменным. А вот содержимое toggle_led() не обозначено. Поэтому утвержтать, что ИАР неправ - преждевременно.
vit496
Цитата(Шаманъ @ Apr 26 2017, 09:25) *
Видимо оптимизатор компилятора не учитывает возможность переполнения, и для него переменная a принимает значения от 5 до 500.

если заменить присваивание a=5 на a=12 (или больше 12), то все работает как надо.

Цитата(k155la3 @ Apr 26 2017, 09:55) *
Внимательно перечитайте все warn

Никаких предупреждений нет.
Код
__root  uint8_t a;

с глобальной переменной работает правильно (без __root - тоже).



сам проект
Шаманъ
Цитата(Сергей Борщ @ Apr 26 2017, 10:01) *
Настолько диких отступлений от стандарта IAR позволять себе не должен.

Так не факт, что это преднамеренно. Но судя по этому:
Цитата(vit496 @ Apr 26 2017, 12:33) *
если заменить присваивание a=5 на a=12 (или больше 12), то все работает как надо.

у IARа таки есть проблема.
vit496
Цитата(GetSmart @ Apr 26 2017, 12:21) *
Удалить if компилятор может как из-за условия, так и из-за тела. Условие обозначено вполне корректное. Даже volatile не требуется переменным. А вот содержимое toggle_led() не обозначено. Поэтому утвержтать, что ИАР неправ - преждевременно.

toggle_led ()
Код
{ GPIOA->ODR  ^= GPIO_Pin_0; }

Еще. Код ниже исправно выводит в порт значения переменной. А условие не выполняется, какой бы код в него не был бы помещен. Его просто нет в листинге.
Код
void main () {
  uint16_t i;
  uint8_t a;

  uart_init(57600);

  a = 5;
  for(i = 0; i < 500; i++) {
    a++;  
    printf("\ra=%d", a);                                    
    if(a == 0) {
      printf("\rxxxxxxx");  
    }
  }

  while(1);
}

в терминале:
Код
...
a=253
a=254
a=255
a=0
a=1
a=2
...


Шаманъ
Цитата(vit496 @ Apr 26 2017, 22:10) *
Еще. Код ниже исправно выводит в порт значения переменной. А условие не выполняется, какой бы код в него не был бы помещен. Его просто нет в листинге.

Вроде уже объяснили - обычный глюк оптимизатора. Пишите понятные программы и они будут работать безглючно.

Как вариант замените a++ на a = (a+1) & 0xFF, ну и расскажите нам заработало ли...
GetSmart
Цитата(vit496 @ Apr 26 2017, 23:10) *
toggle_led ()
Код
{ GPIOA->ODR  ^= GPIO_Pin_0; }

Тут три идентификатора (хотя бы один из них) можно объявить так, что компилятор может иметь право их (иногда) удалять в процессе оптимизации. Вместе с чем-то ещё (проверкой условия). Причём неудаление в какой-то ситуации не означает, что компилятор не имеет право удалить в немного другой ситуации.

А ещё, неожиданно, может оказаться uint8_t шириной более 8 бит и ИАР окажется прав. Но по логу терминала видно, что он 8-битный.
И, если при изменении нач.значения a на 12 код создаётся правильный, то оправдать ИАР не получается. Может оптимизатор на этапе компиляции (почему-то) изменяет ширину переменной до 9 бит, при 12+500 получается как раз 0 в 9-битовой переменной.

PS
Архив не смотрел.
vit496
Цитата(Шаманъ @ Apr 27 2017, 09:59) *
Вроде уже объяснили - обычный глюк оптимизатора. Пишите понятные программы и они будут работать безглючно.

Как вариант замените a++ на a = (a+1) & 0xFF, ну и расскажите нам заработало ли...

нет, ни один из вариантов не помогает, пока переменная 8-ми битная.
условие a == 0x100 - то же не выполняется (на случай, если компилятор считает ее более 8 бит).
Помогает только использовать переменную 16-бит
Код
  uint16_t a=5;
  for(i = 0; i < 500; i++) {
    if(++a >= 0x100) {
      a = 0;
      .....

ну раз глюк, то и ладно. Вопрос можно закрыть. Я уж думал, что вообще ничего не понимаю.
aiwa
Цитата(GetSmart @ Apr 27 2017, 10:07) *
Может оптимизатор на этапе компиляции (почему-то) изменяет ширину переменной до 9 бит, при 12+500 получается как раз 0 в 9-битовой переменной.


Похоже, что Вы правы.
Причем это он делает при операторе а++; И о том, что a - 8-битная он уже вспоминает при сравнении:
Код
??main_0:
        ADDS     R5,R5,#+1
//   44     if(a == 0) {
        UXTB     R0,R5
        CBNZ.N   R0,??main_1
        ......

Но если вместо а++; использовать a+=1; оптимизатор всегда помнит, что a 8-битная и генерирует правильный код:
Код
??main_0:
        ADDS     R5,R5,#+1
        UXTB     R5,R5
//   44     if(a == 0) {
        MOVS     R0,R5
        IT       EQ
        ......


Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.