Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Макрос инкремента с насыщением.
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > GNU/OpenSource средства разработки
adnega
Имеем gcc, пишем на C.

Задача: нужно написать некоторый макрос TICK(x), который бы при каждом вызове увеличивал переменную x на константу TO_TICK, но с учетом переполнения. Т.е. в случае если добавление TO_TICK вызовет переполнение, переменную x не изменять. Тип переменной x может быть любой целый (в том числе битовое поле).

Вариант 1:
Код
#define TICK(x) do{if((x + TO_TICK) > x) x += TO_TICK;}while(0)

не помогает, в случае знакового x и беззнакового TO_TICK. Видимо, (x + TO_TICK) считается беззнаковым и сравнивается с беззнаковым x.

Вариант 2:
Код
#define TICK2(x) do{typedef typeof(x) _tx; _tx _x = x; _x += TO_TICK; if(_x > x) x = _x;}while(0)

в принципе устраивает, но не работает с битовыми полями.

Параллельно узнал, что char без указания знаковости в "gcc version 4.8.0 20121121 (experimental) (Klen's GNU package (KGP) for ARM/elf platform)" является unsigned, хотя везде указывается диапазон char от -128 до 127. В "gcc версия 4.4.3 (GCC)" под Linux char знаковый.

Где нужно читать о типах данных C/GCC и приведении типов в арифметических операциях и операциях сравнения?
Кстати, при различных уровнях оптимизации результат тоже получаю различный. Хочется большей строгости...

Спасет ли переход на C++/GCC ?
_Pasha
Код
#define TICK(x) do{ unsigned char a = x+TO_TICK; if(a > TO_TICK) x=a;}while(0);

и вообще, какого икса дефайнить?
Код
static inline Tick(unsigned char *x)
{
unsigned char a = *x+TO_TICK;
if(a > TO_TICK) *x=a;
}
adnega
Цитата(_Pasha @ Dec 16 2012, 20:59) *
и вообще, какого икса дефайнить?

Чтобы работало для разных типов на автомате.
_Pasha
Цитата(adnega @ Dec 16 2012, 20:46) *
Чтобы работало для разных типов на автомате.

Во! А теперь еще более интересный вопрос: у Вас "тикающий тип" - это одна сущность или много? Вернее, почему нельзя создать тип нужной размерности, скажем typedef unsigned int tick_t
Если на то есть причины, расскажите, плз, очень интересно.
adnega
Цитата(_Pasha @ Dec 16 2012, 22:26) *
Во! А теперь еще более интересный вопрос: у Вас "тикающий тип" - это одна сущность или много? Вернее, почему нельзя создать тип нужной размерности, скажем typedef unsigned int tick_t
Если на то есть причины, расскажите, плз, очень интересно.

А то!
Раньше было много, сейчас, видимо, придется переходить на одну.
В целях экономии памяти использую различную разрядность "тикающих типов", в идеале вплоть до битовых полей.
_Pasha
Цитата(adnega @ Dec 16 2012, 21:29) *
А то!
Раньше было много, сейчас, видимо, придется переходить на одну.
В целях экономии памяти использую различную разрядность "тикающих типов", в идеале вплоть до битовых полей.

В таком случае (мсм) - проще функцию инлайновую завести, в аргументе const tick_t
Эффектов побочных от битовых полей не ожидается. При желании наплевать на контроль типов - обернуть функцию в дефайн. Вроде чуть больше строк, но просто и работать должно. Но не проверял laughing.gif

PS inline tick_t Tick(const tick_t tikable){}
adnega
Цитата(adnega @ Dec 16 2012, 20:37) *
Вариант 2:
Код
#define TICK2(x) do{typedef typeof(x) _tx; _tx _x = x; _x += TO_TICK; if(_x > x) x = _x;}while(0)

в принципе устраивает, но не работает с битовыми полями.

Не работает при вызове TICK2 в SysTick при оптимизации Os, хотя при использовании во вложенных функциях, вызываемых из SysTick - работает.
Блин...
Сделаю выделенный "тикающий тип" и заведу функцию для контроля передаваемого типа.
_Артём_
Цитата(adnega @ Dec 16 2012, 20:52) *
Сделаю выделенный "тикающий тип" и заведу функцию для контроля передаваемого типа.

Почему перегрузку функций не использовать?
adnega
Цитата(adnega @ Dec 16 2012, 22:52) *
Не работает при вызове TICK2 в SysTick при оптимизации Os, хотя при использовании во вложенных функциях, вызываемых из SysTick - работает.
Блин...
Сделаю выделенный "тикающий тип" и заведу функцию для контроля передаваемого типа.


Пока сделаю так:

Код
typedef volatile unsigned long tTIMER;

void __inline tick(tTIMER *t)
{
    tTIMER temp;
    temp = *t;
    temp += TO_TICK;
    if(temp > (*t)) *t = temp;
}

tTIMER     test_ttimer; // тестовая переменная

tick(&test_ttimer);  // вызов функции тика


Причем "тикать" строго из одного потока. Читать таймер из любого множества потоков.

Цитата(_Артём_ @ Dec 16 2012, 23:09) *
Почему перегрузку функций не использовать?

Потому что чистый C.
_Артём_
Цитата(adnega @ Dec 16 2012, 21:14) *
Потому что чистый C.

Зачем эта "чистота"?
_Pasha
Попутно еще замечание, что параметр функции const утаптывается оптимизатором лучше, и если пользоваться
Код
tTIMER __inline tick(const tTIMER tmr)
{
    tTIMER temp= tmr+TO_TICK;
    return (temp > TO_TICK)? temp:tmr;
}

tTIMER     test_ttimer; // тестовая переменная
test_ttimer = tick(test_ttimer);

может быть лучше. Вот сравните без volatile, если интересно, конечно sm.gif
polyname
может лучше сравнивать с разностью ?
Код
#define ADD_LIMIT(x,dx,max) x = ((x) < ((max) - (dx))) ? ((x) + (dx)) : (max)
andrew_b
Цитата(adnega @ Dec 16 2012, 20:37) *
Параллельно узнал, что char без указания знаковости в "gcc version 4.8.0 20121121 (experimental) (Klen's GNU package (KGP) for ARM/elf platform)" является unsigned, хотя везде указывается диапазон char от -128 до 127. В "gcc версия 4.4.3 (GCC)" под Linux char знаковый.

Где нужно читать о типах данных C/GCC и приведении типов в арифметических операциях и операциях сравнения?
Читать нужно стандарт языка Си.
Цитата
15 The three types char, signed char, and unsigned char are collectively called
the character types. The implementation shall define char to have the same range,
representation, and behavior as either signed char or unsigned char.

Иными словами, знаковость типа char зависит от реализации.
ARV
#define TICK(x) x = ((x) + TO_TICK) < (x) ? (x) : ((x) + TO_TICK)

может так?
ViKo
Цитата(adnega @ Dec 16 2012, 19:37) *
Задача: нужно написать некоторый макрос TICK(x), который бы при каждом вызове увеличивал переменную x на константу TO_TICK, но с учетом переполнения. Т.е. в случае если добавление TO_TICK вызовет переполнение, переменную x не изменять.

А вам точно нужно это? Может, лучше - с ограничением?
#define VAR_INC(x, step, max) x = ((x + step)>max)? max : (x + step)
adnega
Цитата(ViKo @ Dec 17 2012, 23:03) *
А вам точно нужно это? Может, лучше - с ограничением?
#define VAR_INC(x, step, max) x = ((x + step)>max)? max : (x + step)

Не пойдет(
Это для каждого счетчика нужно "придумывать" свое ограничение. Причем изменив разрядность (тип) счетчика придется ручками по всему коду править ограничения - а это источник ошибок.
Хотелось на автомате...
Но за неумением "объяснить" свои намеренья оптимизирующему компилятору придется вводить единый тип для всех "тикалок".
Кста, Ваша идея мне нравится и везде я стараюсь пользоваться конструкциями

Код
TICK(timeout);
if(timeout >= TO_TIMEOUT)
{
  state = NEW_STATE;
  timeout = 0;
}


или

Код
if(timeout < TO_TIMEOUT) TICK(timeout);


Что так или иначе ограничивает увеличение timeout, но второй вариант можно использовать только если ограничение какое-то одно (фиксированное), а сам таймер предназначен для ожидания какого-то одного события. Если это не так, то использую вариант 1.
ReAl
Цитата(adnega @ Dec 16 2012, 18:37) *
не помогает, в случае знакового x и беззнакового TO_TICK. Видимо, (x + TO_TICK) считается беззнаковым и сравнивается с беззнаковым x.
Конечно, если TO_TICK явно беззнаковое, то в (x + TO_TICK) сначала будет приведение x к беззнаковому.
А зачем TO_TICK беззнаковое? Оно же явно должно влазить по диапазону в знаковые числа, а при (unsignedX + signedTICK) уже тик приведётся к беззнаковому и будет все нормально.
Хотя с битовыми полями и прочими заужениями до байтов все равно будет плохо, так как в + сначала обе стороны приведутся к int и для uint8_t всё равно всегда ((255+1) > 255) истинно и прибавление тика не остановится.

Кстати, с typeof можно и проще
Код
#define TICK(x) do { if ( (typeof(x))(x+TO_TICK) > x ) x += TO_TICK;}while(0)

но с битовыми полями так же не будет работать. Хотя я в таких местах ниже байта не опускаюсь, битовые поля - счётчики немного экономят ОЗУ, но раздувают код (Cortex-M3-шные команді работы с битовыми полями штука хорошая, но не везде есть).

И лучше уж C++ шаблонную функцию, чем такой макрос. Не вижу препятствий работать в процедурном стиле даже без простейших классов, но с такого рода шаблонами. А для кого есть gcc, для того есть и g++.
adnega
Цитата(ReAl @ Dec 18 2012, 17:02) *
Конечно, если TO_TICK явно беззнаковое, то в (x + TO_TICK) сначала будет приведение x к беззнаковому.
А зачем TO_TICK беззнаковое? Оно же явно должно влазить по диапазону в знаковые числа, а при (unsignedX + signedTICK) уже тик приведётся к беззнаковому и будет все нормально.

Я-то боюсь, что "умный" оптимизатор вообще вправе выкинуть финт: если к некоторому числу прибавить положительную константу, то результат (чисто в математическом смысле) будет БОЛЬШЕ исходного числа - поэтому условие всегда истинное. А если программисту нужна такая "платформенная вещь" как "переполнение", то либо врукопашную оптимизируй, либо научись грамотно указать оптимизатору свои намеренья.

Вывод для себя делаю такой: если переполняется какая-то переменная, то алгоритм можно считать плохим. На этапе проектирования не нужно допускать неограниченного инкремента "тикающих" переменных (справедливо для всех случаев, кроме измерителя uptime, но тут нужно наращивать разрядность, либо вводить масштаб).

Для битовых полей написать специальный макрос/функцию для выполнения этой конкретной задачи.

Вообще, это скорее проблема программиста (моя), а не инструмента: хочется чтобы программа сама себя писала, но для решения данной проблемы есть ДРУГИЕ инструменты, которыми и нужно пользоваться.
scifi
Цитата(adnega @ Dec 20 2012, 14:40) *
Я-то боюсь, что "умный" оптимизатор вообще вправе выкинуть финт: если к некоторому числу прибавить положительную константу, то результат (чисто в математическом смысле) будет БОЛЬШЕ исходного числа - поэтому условие всегда истинное.

Зря боитесь, не будет такого. Компилятор всё делает по стандарту языка Си (с точностью до глюков компилятора, но мы же не о них сейчас говорим). В стандарте четко прописана семантика переполнения целочисленной переменной.

Цитата(adnega @ Dec 20 2012, 14:40) *
Вообще, это скорее проблема программиста (моя), а не инструмента: хочется чтобы программа сама себя писала, но для решения данной проблемы есть ДРУГИЕ инструменты, которыми и нужно пользоваться.

Я с самого начала это хотел сказать. Мне кажется, необходимость насыщения должна возникать редко, поэтому и макрос тут ни к чему.
_Pasha
Мне интересно другое. А насыщение в виде равенства нулю? Зачем инкремент, если можно декремент?
Шаманобубен какой-то...
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.