Вопросы по С/С++ (СИ)

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить
Друг Кота
Аватара пользователя
Сообщения: 6348
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Сообщение Jack_A »

[uquote="jcxz",url="/forum/viewtopic.php?p=4543105#p4543105"].Если для вас толпа скобок в строке улучшает читаемость, то для другого - ухудшает.[/uquote]
Типичный пример демагогии: утверждение оппонента преувеличить до бесконечности, доведя до абсурда. Я не практиковал "толпу скобок", равно как и выражение на пол-страницы. Разбивал сложное выражение на части, использовал вспомогательные переменные, благо дефицита оперативной памяти не наблюдается. Трудно представить программу, где все вычисленные части выражения используются только однократно, практически в разных местах кода можем использовать уже вычисленные фрагменты. И скобки нисколько не мешали.
Бессмысленное утверждение
это пустой трёп.
Чувствуется рука мастера. Э.Дейкстра, это Вы?? :)) :shock:
Изображение
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

[uquote="Bill_",url="/forum/viewtopic.php?p=4543375#p4543375"]Или нет?[/uquote]А вы как думаете? Свернёт или нет?
Реклама
Открыл глаза
Сообщения: 60
Зарегистрирован: Вс ноя 13, 2022 14:58:17

Сообщение Bill_ »

[uquote="VladislavS",url="/forum/viewtopic.php?p=4543404#p4543404"][uquote="Bill_",url="/forum/viewtopic.php?p=4543375#p4543375"]Или нет?[/uquote]А вы как думаете? Свернёт или нет?[/uquote]
Если написать так: display % (1000/100), то сворачивает. Если скобки убрать, то - нет. Компилятор - IAR STM8.
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

А как правильно? Сворачивать или нет?

Добавлено after 1 minute 26 seconds:
И полагаете ли вы что со скобками это то же самое выражение?
Реклама
Эиком - электронные компоненты и радиодетали
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

Конечно же нет

Правильный результат для display % 1000 /100

Код: Выделить всё

12345 % 1000 /100 = 345 / 100 = 3
Если внезапно (с чего бы вдруг) это сворачивать до display % 10, получим просто 5.

Другое дело, что хороший компилятор может это упростить до display / 100 % 10

Код: Выделить всё

12345 / 100 % 10 = 123 % 10 = 3
Результат будет всегда верным, но числа немного меньшие - вычисления могут быть теоретически быстрее.

P.S. Но вообще, ранее приведённый код - плохой, так как в нём вычисления для каждого разряда разные:

Код: Выделить всё

PORTD = ~((SEGMENTE[display % 1000/100])); 
PORTD = ~(SEGMENTE[display % 100/10]);
PORTD = ~(SEGMENTE[display % 10]);
Лучше выводить числа справа налево, тогда там для любого разряда нужно делать лишь %10 и /10, то есть, что-то вроде:

Код: Выделить всё

PORTD = ~(SEGMENTE[display % 10]); # разряд единиц
display /= 10;
PORTD = ~(SEGMENTE[display % 10]); # разряд десятков
display /= 10;
PORTD = ~(SEGMENTE[display % 10]); # разряд сотен
display /= 10;
PORTD = ~(SEGMENTE[display % 10]); # разряд тысяч
Такой подход и в цикл легко превратить, и работает с любым количеством разрядов...

P.P.S.

Вот пример такого подхода уменя - как-то делал по-быстрому по просьбе человека, который где-то разобыл табло для электронных очередей (с большими семисегментниками) и хотел сделать из этого часы.
Там ещё в этой функции и нули лишние не рисуются, если число маленькое получилось, и знак минуса поддерживается.
Последний раз редактировалось WiseLord Ср фев 14, 2024 20:58:36, всего редактировалось 3 раза.
Контактная информация:
Реклама
Мудрый кот
Сообщения: 1838
Зарегистрирован: Вт авг 15, 2017 10:51:13

Сообщение jcxz »

[uquote="Bill_",url="/forum/viewtopic.php?p=4543375#p4543375"]Поэтому данное выражение компилятор вероятно сведёт к выражению display % 10. Или нет?[/uquote]Да ужж.... не думал, что на данном форуме так плохо с математикой начальных классов средней школы... печалька :dont_know:

Добавлено after 8 minutes 5 seconds:
[uquote="WiseLord",url="/forum/viewtopic.php?p=4543527#p4543527"]Лучше выводить числа справа налево, тогда там для любого разряда нужно делать лишь %10 и /10, то есть, что-то вроде:

Код: Выделить всё

PORTD = ~(SEGMENTE[display % 10]); # разряд единиц
display /= 10;
PORTD = ~(SEGMENTE[display % 10]); # разряд десятков
display /= 10;
PORTD = ~(SEGMENTE[display % 10]); # разряд сотен
display /= 10;
PORTD = ~(SEGMENTE[display % 10]); # разряд тысяч
Такой подход и в цикл легко превратить, и работает с любым количеством разрядов...[/uquote]Ваш код тоже не ахти.
Зачем для каждого разряда 2 деления? Деление - плохая операция. Даже на тех МК, где она аппаратно есть, выполняется очень медленно.
Всё ведь просто:

Код: Выделить всё

uint i0 = display / 10;
uint digit0 = display - i0 * 10;
uint i1 = i0 / 10;
uint digit1 = i0 - i1 * 10;
...
Всего по одному делению на разряд. На ARM такой код будет гораздо быстрее.
Реклама
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

jcxz писал(а):Зачем для каждого разряда 2 деления? Деление - плохая операция. Даже на тех МК, где она аппаратно есть, выполняется очень медленно.
Потому что это:
- легко читается и понимается
- может оптимизироваться компилятором так, что делений там и не будет.
- такой код вполне уместен при выводе результатов на экран, будучи вызываем один раз в главном цикле. В критичных местах вроде прерываний - да, делить лучше не стоит.

Например, анализируя листинг, я как-то видел что деление 8-битного числа 'num' на константу 10 компилятор обычно преобразует в умножение на 205 и сдвиг:

Код: Выделить всё

// Например, num = 234

// Написано программистом 
digit = num % 10         # digit = 234 % 10 = 4
num /=10                 # num = 234 / 10 = 23

// Оптимизировано компилятором 
tmp = (num * 205) << 11; # tmp = (234 * 205) << 11 = 47970 << 11 = 23
digit = num - tpm * 10;  # digit = 234 - 23 * 10 = 234 - 230 = 4
num = tmp;               # num = 23
Что за магическое число 205?

Деление числа на 10 равносильно его умножению на 204.8 и последующему делению на 2048 (он же сдвиг вправо на 11 разрядов). А если умножать не на 204.8, а на 205 - результат для целых чисел будет эквивалентным, по крайней мере в диапазоне 0..255 (P.S. а на самом деле - даже в диапазоне 0..1028).

Для чисел большей разрядности можно тоже подобрать (и компиляторы это часто умеют) такие числа, которые будут давать правильный результат.

P.S.

Кстати, попробовал ваш совет применить в вышеуказанном проекте, и... фиаско:

"До" - размер бинарника прошивки 1290 байт:

Код: Выделить всё

    for (i = 0; i < DIGITS; i++) {
        if (number == 0 && i > dot)
            break;
        ind[i] = num[number % 10];
        number /= 10;
    }
После - размер бинарника прошивки 1372 байта:

Код: Выделить всё

    for (i = 0; i < DIGITS; i++) {
        if (number == 0 && i > dot)
            break;
        uint16_t i10 = number / 10;
        ind[i] = num[number - i10 * 10];
        number = i10;
    }
После "оптимизации" прошивка стала больше на 82 байта.

P.P.S.

Целочисленное деление на 10 вовсе не добавляет "ещё одно деление". Раз уж при вычислении остатка от деления число уже было один раз поделено на 10 (и промежуточный результат деления был помещён в какой-то регистр), второй раз комплиятор этого делать не будет, и num /= 10 сведётся просто к забиранию из этого регистра готового результата.
Контактная информация:
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

WiseLord, код не оптимизируют по размеру бинарника. Надо листинг для целевой платформы смотреть.
Мудрый кот
Сообщения: 1838
Зарегистрирован: Вт авг 15, 2017 10:51:13

Сообщение jcxz »

[uquote="WiseLord",url="/forum/viewtopic.php?p=4543605#p4543605"]Кстати, попробовал ваш совет применить в вышеуказанном проекте, и... фиаско:

"До" - размер бинарника прошивки 1290 байт:

Код: Выделить всё

    for (i = 0; i < DIGITS; i++) {
        if (number == 0 && i > dot)
            break;
        ind[i] = num[number % 10];
        number /= 10;
    }
После - размер бинарника прошивки 1372 байта:

Код: Выделить всё

    for (i = 0; i < DIGITS; i++) {
        if (number == 0 && i > dot)
            break;
        uint16_t i10 = number / 10;
        ind[i] = num[number - i10 * 10];
        number = i10;
    }
После "оптимизации" прошивка стала больше на 82 байта.[/uquote]Почти 1.5 КБ на таком простейшем коде??? Серьёзно??? :shock: :shock: :shock:
Как вы такого добились?
Попробовал скомпилить в IAR:

Код: Выделить всё

enum {DIGITS = 6};
static u8 ind[DIGITS];
static u8 const num[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
#if 0
void f1(uint number, uint dot)
{
  for (uint i = 0; i < DIGITS; i++) {
    if (number == 0 && i > dot) break;
    ind[i] = num[number % 10];
    number /= 10;
  }
}
#else
void f2(uint number, uint dot)
{
  for (uint i = 0; i < DIGITS; i++) {
    if (number == 0 && i > dot) break;
    uint i10 = number / 10;
    ind[i] = num[number - i10 * 10];
    number = i10;
  }
}
#endif
Результаты:
1-й вариант:

Код: Выделить всё

          void f1(uint number, uint dot)
          {
            for (uint i = 0; i < DIGITS; i++) {
                  _Z2f1jj: (+1)
00000000   0x2200             MOVS     R2,#+0
00000002   0x230A             MOVS     R3,#+10
              if (number == 0 && i > dot) break;
                  ??f1_0: (+1)
00000004   0xB908             CBNZ.N   R0,??f1_1
00000006   0x4291             CMP      R1,R2
00000008   0xD304             BCC.N    ??f1_2
              ind[i] = num[number % 10];
              number /= 10;
            }
                  ??f1_1: (+1)
0000000A   0x1C52             ADDS     R2,R2,#+1
0000000C   0x2A06             CMP      R2,#+6
0000000E   0xFBB0 0xF0F3      UDIV     R0,R0,R3
00000012   0xD3F7             BCC.N    ??f1_0
          }
                  ??f1_2: (+1)
00000014   0x4770             BX       LR               ;; return
2-й:

Код: Выделить всё

          void f2(uint number, uint dot)
          {
            for (uint i = 0; i < DIGITS; i++) {
                  _Z2f2jj: (+1)
00000000   0x2200             MOVS     R2,#+0
00000002   0x230A             MOVS     R3,#+10
              if (number == 0 && i > dot) break;
                  ??f2_0: (+1)
00000004   0xB908             CBNZ.N   R0,??f2_1
00000006   0x4291             CMP      R1,R2
00000008   0xD304             BCC.N    ??f2_2
              uint i10 = number / 10;
              ind[i] = num[number - i10 * 10];
              number = i10;
            }
                  ??f2_1: (+1)
0000000A   0x1C52             ADDS     R2,R2,#+1
0000000C   0x2A06             CMP      R2,#+6
0000000E   0xFBB0 0xF0F3      UDIV     R0,R0,R3
00000012   0xD3F7             BCC.N    ??f2_0
          }
                  ??f2_2: (+1)
00000014   0x4770             BX       LR               ;; return
Как видно - всего 22 байта как в 1-м так и во 2-м случае. Как у вас получилось почти 1.5 КБ - ума не приложу.

Да, в этом случае компилятор догадался, что команду UDIV можно использовать только одну. И в этом случае разницы между вариантами нет. Но компилятор не всегда такой догадливый. В более сложных функциях он не всегда догадывается до этого. И тогда вариант 2 получается и меньше и быстрее.
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

Пятничная затравка Код.

Для беззнаковых

Код: Выделить всё

void Num2Segs(std::unsigned_integral auto number)
{
  for(auto i=DIGITS; i; )
  {   
    if(number || i==DIGITS)
    {
      auto dv = div(number,10);
      ind[--i] = Dig2Seg[dv.rem];
      number = dv.quot;
    }
    else ind[--i] = 0;
  }  
}
Перегрузка для знаковых

Код: Выделить всё

void Num2Segs(std::signed_integral auto number)
{
  for(auto &x : ind) x=0;  
  bool neg = number<0;
  auto tmp = neg ? -number : number;
  for(auto i=DIGITS; i>(neg?1:0); )
  {   
    if(tmp || i==DIGITS)
    {
      auto dv = div(tmp,10);
      tmp = dv.quot;      
      ind[--i] = Dig2Seg[dv.rem];
      if(neg && (tmp==0 || i==1))
      {
        ind[--i]= Dig2Seg[10]; // минус
        break;
      }      
    }
    else --i;
  }
}
Мудрый кот
Сообщения: 1838
Зарегистрирован: Вт авг 15, 2017 10:51:13

Сообщение jcxz »

[uquote="VladislavS",url="/forum/viewtopic.php?p=4544156#p4544156"]Пятничная затравка Код.

Для беззнаковых
...
Перегрузка для знаковых
...[/uquote]Ну всё - блоха подкована. :)))
Лишь бы скакать после этого смогла. 8)
Последний раз редактировалось jcxz Пт фев 16, 2024 18:20:29, всего редактировалось 1 раз.
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

Скорее воробьи постреляны.
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

jcxz писал(а):Почти 1.5 КБ на таком простейшем коде??? Серьёзно??? :shock: :shock: :shock:
Нет, конечно. Это целиком весь проект часов https://github.com/WiseLord/clock7segm

Смотрите не на абсолютное значение, а на разницу.
Контактная информация:
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

WiseLord, хотел посмотреть откуда 82 байта, а функция segmNum в коде вообще не используется.

Добавлено after 1 hour 13 minutes 40 seconds:
Забил функцию в Compiler Explorer. Было.

Немного поработал с типами. Стало.

Вот теперь можно попробовать разные варианты. Не забыть оптимизацию O2 и O3 попробовать - там интереснее.
Друг Кота
Аватара пользователя
Сообщения: 4905
Зарегистрирован: Чт апр 11, 2013 11:19:59
Откуда: Минск

Сообщение WiseLord »

[uquote="VladislavS",url="/forum/viewtopic.php?p=4544645#p4544645"]WiseLord, хотел посмотреть откуда 82 байта, а функция segmNum в коде вообще не используется.[/uquote]
Да, функция не используется именно в этом проекте, просто в библиотеке она есть. Для проверки добавлял её вызов в main.c сразу после segmTime(). Ну и ещё на -flto отключал.
Контактная информация:
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

В AVR-ке деления через библиотечные функции. Для каждого типа (разрядности и знаковости) своя функция. Шаг в сторону с выбором типа и плюс дополнительная функция прилинковывается к коду. Отсюда и скачки в размере прошивки. Повторюсь, надо смотреть листинг в каждом конкретном случае когда оптимизацией занимаешься.
Это не хвост, это антенна
Аватара пользователя
Сообщения: 1393
Зарегистрирован: Чт авг 21, 2014 11:11:48
Откуда: краснодарский край

Сообщение главный колбасист »

А куда в шаблоне для окна windows из Dev-C++ надо засовывать свой код?
Чтобы выполнилось в этом окне.
Я куда ни сую его,компилируется с ошибками, а если ничего никуда не совать,
ошибок нет,но выполняется файл exe просто как пустое окно.
В какую щель между строками я должен вставить свой код ?
Вложения
шаблон.txt
(2.35 КБ) 83 скачивания
Контактная информация:
Первый раз сказал Мяу!
Сообщения: 25
Зарегистрирован: Ср июн 03, 2015 21:39:14

Сообщение Selfrock »

Ну, можно так...
Вложения
Win_templ.cpp
(2.52 КБ) 94 скачивания
Это не хвост, это антенна
Аватара пользователя
Сообщения: 1393
Зарегистрирован: Чт авг 21, 2014 11:11:48
Откуда: краснодарский край

Сообщение главный колбасист »

Да, так получилось.
з.ы.
А почему такой файл .exe огромный,под 1мб. не многовато ли для вывода двух слов ?
Контактная информация:
Первый раз сказал Мяу!
Сообщения: 25
Зарегистрирован: Ср июн 03, 2015 21:39:14

Сообщение Selfrock »

Видимо ваш компилятор пристегивает к файлу, какие то, нужные с его точки зрения либы, у меня этот файл после компиляции g++ v.11.2 x64 имеет размер 49 кб без отладочной информации и 54 кб с нею...
Ответить

Вернуться в «Разные вопросы по МК»