Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.55/55: Рейтинг темы: голосов - 55, средняя оценка - 4.55
 Аватар для Goose45
2 / 2 / 1
Регистрация: 30.07.2016
Сообщений: 118

Вычитание беззнаковых чисел

30.08.2017, 20:33. Показов 12285. Ответов 29
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
При выполнении вот этого кода ,у меня вывод 4294967292 ,не могу понять почему ,помогите пожалуйста ,дайте ссылку на какую нибудь инфу по этой теме

C++
1
2
3
4
5
6
7
8
9
#include <iostream>
 
 
int main()
{
    std::cout << 4u - 8;
    system("pause");
    return 0;
}
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
30.08.2017, 20:33
Ответы с готовыми решениями:

Сравнение знаковых и беззнаковых чисел
Возьмем пример: #include &lt;stdio.h&gt; int main() { if ( 1U &gt; -1 ) printf(&quot;1 &gt; -1&quot;); else printf(&quot;1 &lt;= -1\n&quot;);...

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

С клавиатуры последовательно ввести 8 длинных беззнаковых чисел
С клавиатуры последовательно ввести 8 длинных беззнаковых чисел. Определить среди них число, сумма цифр которого самая большая.

29
56 / 20 / 2
Регистрация: 18.06.2018
Сообщений: 199
20.02.2019, 10:32
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Evg Посмотреть сообщение
Это очевидное решение в лоб. Честно говоря, мне даже в голову не могло прийти, что ты до него не смог додуматься. После фразы "Совершенно понятно" мне показалось, что ты понимаешь, что тут творится
Что тут творится мне стало ясно не совсем сразу .. пришлось полазить по стандарту и дойти до 7.6 "integer promotion"
К этому времени автор сам допер, чисто научным тыком до явного приведения результата - помогло.

Цитата Сообщение от Evg Посмотреть сообщение
Не будет работать что именно? В том смысле, что предмет вопроса не совсем понятен. Возможно, имелось в виду, что если сравнивать unsigned char с unsigned (а именно такой тип имеет выражение "25u"), то какие-то компиляторы могут выдать предупреждение. Может имелось в виду, что если a<b, то a-b будет иметь большое значение из-за представления в дополнительном коде, которое окажется больше, чем 25u

Добавлено через 30 секунд
Другими словами, народную мудрость "внятно поставленный вопрос - это уже половина ответа" никто не отменял
В том Solution (не знаю где он его откопал, мне пришла только цитата) сказано что явное приведение типа результата может не всегда "спасать положение", а вот в каких случаях оно бесполезно - цитаты не было, вот и озадачился в каких?

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

Исследуя вопрос (просматривая результат компиляций от AVR-G++) пока наткнулся на это:

uint8_t i;
uint8_t *ptr;
/*...*/
for( i=0; i<4; i++, ptr++){
/*..*/
}

игнорит явно заданный байтовый счетчик цикла и делает его целым, и ещё (пока не понял) похоже дополнительно генерирует скрытый целый счетчик дополнительно для контроля за ptr. Цикл завершается весьма странно по проверке ptr, а не i. Уровень оптимизации Os.
Но ещё дизассемблирую код, поскольку такое возникло на не простой функции и 2-х вложенных циклах. На одиноком for() похоже счетчик таки байтовый. Пытаюсь понять не накосячил ли где в коде на С, а то вдруг "зря наезжаю".

В общем, вопрос встал в такой форме: КАК можно гарантировать применение именно байтовой арифметики для 8-и разрядных микроконтроллеров для байтовых типов данных?
Дело в том, что в С/С++ просто "вагон" разных операций, которые в общем-то все связаны с явным применением укороченных команд типа "инкремент", "декремент" и т.д., что есть у большинства ЦПУ. 8-и битники их имеют но .. в байтовых данных, а вот со словами набор команд часто сильно урезан и получается что использовать "всю прелесть С" - становится нереальным. из-за integer promotions.

Так, в частности для AVR: словных команд только пересылка и сложение/вычитание. Даже чтобы положить на регистры словную константу зачастую надо использовать промежуточный регистр (не все позволяют) И 4 команды.

Пока что нашел некое замечание, что компилятору можно как-то указать какой размер у "базового целого", но там же примечание что это приведет к проблемам .. тоже пока разбираюсь.

Добавлено через 3 часа 16 минут
В общем, разобрал тот код .. итого "цикл for есть зло для микроконтроллера" do{}while() - рулит.

А вот рекомендация из Апноты AVR035 за то, что все лучше пихать в "объект" - оказалась не верна. Россыпь глобалов дает меньший размер кода. И ещё: работа через указатели не всегда приносит пользу. Обращение kbdKeys[num] к глобалу оказалось компактней и менее затратным.
Но, это так .. "побочный результат".

А по итогу, да подтвердилось что локальный счетчик for запросто может вырасти до int и ничего с этим не сделать. "Скрытым" оказалась граница указателя и, несмотря на то что у цикла есть счетчик, завершение for определялось по указателю .. несколько странно, но надежно.

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

Мой вывод: хотите писать эффективно и компактно для микроконтроллера - пользуйте Асм. Ничего иного нет. Можно легко сваять прототип на С/С++ и потом допиливать его в Асм, тем более что это не трудно. Оптимизация кода под AVR пока ещё оставляет желать лучшего. В свое время Watcom C справлялся не в пример лучше, ИМХО, так отложилось в памяти.

В общем, для себя тему закрыл. Новичкам НАДО тщательно разжевывать integer promotions иначе будут грабли.
0
Evg
20.02.2019, 10:39

Не по теме:

Цитата Сообщение от Verevkin Посмотреть сообщение
Это почему жэ? Вычитание (32 бит) x - y = 1 + x + ~y;
Вопрос был вообще не об этом

0
Злостный нарушитель
 Аватар для Verevkin
10878 / 5817 / 1288
Регистрация: 12.03.2015
Сообщений: 26,855
20.02.2019, 10:43
Цитата Сообщение от Evg Посмотреть сообщение
Вопрос был вообще не об этом
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
20.02.2019, 10:47
Цитата Сообщение от Arhat109 Посмотреть сообщение
В общем, вопрос встал в такой форме: КАК можно гарантировать применение именно байтовой арифметики для 8-и разрядных микроконтроллеров для байтовых типов данных?
Если говорить о стандартном Си, то никак. Но в микроконтроллерах могут использоваться какие-нибудь расширения, так что я тут ничего не могу сказать

Кстати, операции ++ и -- (возможно, ещё и += и -=) выполняются без promotion

Сам по себе promotion не страшен, ибо компилятор всё равно всё соптимизирует. А если машина поддерживает байтовые операции, будет работать именно с ними. Реальная проблема только в warning'ах. Ещё проблему (производительности) может создавать то, что конкретный компилятор под конкретный процессор может не справиться с определённым видом оптимизаций. Но это уже несколько выходит за рамки языка, а упирается в конкретную реализацию. Т.е. требуется опыт работы именно на конкретном компиляторе, с пониманием всех его недоделок

Про твой цикл for тоже сложно что-то сказать, ибо в нём вырезаны внутренности, а в тексте ты ссылаешься на них. В итоге можно только гадать, о чём идёт речь

Добавлено через 1 минуту

Не по теме:

Verevkin, на твоём скриншоте видно, что задали вопрос про корову, а ты отвечаешь про шарики

0
56 / 20 / 2
Регистрация: 18.06.2018
Сообщений: 199
20.02.2019, 11:05
вопрос возник с этой функцией:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// сканер - проверяльщик текущего состояния кнопок на момент вызова:
void kbdScan()
{
  uint8_t _time = kbdMillis();
  kbdCountPressed = kbdCountLonged = kbdCountUpped = 0;
 
  for( int8_t row=0; row<KBD_MAX_ROWS; row++)  // сканируем строку
  {
    digitalWrite(kbdRows[row], LOW);           // прижимаем строку в "0"
    for(int8_t col=0; col<KBD_MAX_COLS; col++) // ищем нажатую кнопку в строке
    {
      uint8_t keyNum = row*KBD_MAX_ROWS + col;
      uint8_t keyVal = kbdKeys[keyNum];
 
      if(
          !(keyVal & KBD_KEY_PRESS) ||                            // кнопка ещё не нажата или
          (keyVal & KBD_KEY_BOUNCE) ||                            // уже прошла дребезг или
          ((uint8_t)(_time - kbdTime(keyVal)) > KBD_TIME_BOUNCE)  // (приведение обязательно!) вышло время дребезга
      ){
        // далее только для не нажатых кнопок или у которых уже прошла проверка дребезга
 
        if( digitalRead(kbdCols[col]) == LOW){                                // кнопка нажата:
          if( !(keyVal & KBD_KEY_PRESS) ){                                    // ..только что нажата?
            keyVal = KBD_KEY_PRESS | _time;                                   // ..и время для дребезга
          }else
            if( !(keyVal & KBD_KEY_BOUNCE) ){                                 // ..нажата, но проверяется дребезг?
              keyVal = KBD_KEY_PRESS | KBD_KEY_BOUNCE | _time;                // ..новое время для удержания
              kbdJustPressed[kbdCountPressed++] = keyNum;
            }else
              if( !(keyVal & KBD_KEY_LONG) ){                                 // .. проверка удержания?
                if( (uint8_t)(_time - kbdTime(keyVal)) >= KBD_TIME_LONG ){
                  keyVal = KBD_KEY_PRESS | KBD_KEY_BOUNCE | KBD_KEY_LONG;     // .. теперь "удерживается"
                  kbdJustLonged[kbdCountLonged++] = keyNum;
                }else{
                  kbdJustPressed[kbdCountPressed++] = keyNum;                 // .. все ещё PRESSED
                }
              }else{
                kbdJustLonged[kbdCountLonged++] = keyNum;                     // .. всё ещё LONG
              }
        }else{                                                                // кнопка отпущена:
          if( keyVal == KBD_KEY_FREE ){ continue; }                           // .. и не была нажата. Пропуск.
          if( (keyVal & (KBD_KEY_BOUNCE | KBD_KEY_LONG)) != 0 ){              // .. отпуск нажатой или удерживаемой
            kbdJustUpped[kbdCountUpped++] = keyNum;
          }
          keyVal = KBD_KEY_FREE;
        }
        kbdKeys[keyNum] = keyVal;
      }
    } //end col
    digitalWrite(kbdRows[row], HIGH); // отжимаем строку обратно
  } // end row
}
Оба цикла повторяются не более 4 раз (клавиатура Ардуино 4х4 кнопки). Тут автором была заложена идея сохранения времени антидребезга в одном байте, вместе с флагами состояния каждой кнопки. То есть на клавиатуру 4х4 расходуется всего 16 байт, которые в других реализациях заняты и так состояниями кнопки (пример библиотека для Ардуино: amperkaKB) их все равно надо хранить. А вот контроля антидребезга в ней нет и предлагается делать "поверх" сканера кнопок.

Вот, если делать байтовый контроль времени антидребезга и удержаний, то возникает поднятая проблема: расширение байтового целого до int приводит к совсем не правильной работе условий if( _time - kbdTime() > interval ){ .. }

Им же было найдено решение по явному приведению типа промежуточного результата вычитания к беззнаковому байту. Такие условные операторы в Ардуино для контроля временных интервалов используются "сплошь и рядом", но они во всех примерах на unsigned long, что никаких проблем не вызывает. Хранить для каждой кнопки длинное целое без знака .. ну как-бы "роскошь"

А то, что урезание приведет к неверной работе - просто не ожидалось, вот и возник вопрос "где ещё может скрываться подвох".
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
20.02.2019, 22:09
В этом коде нет циклов, аналогичных тому, который ты привёл в посте #21 и о которых в нём говорил

Цитата Сообщение от Arhat109 Посмотреть сообщение
Вот, если делать байтовый контроль времени антидребезга и удержаний, то возникает поднятая проблема: расширение байтового целого до int приводит к совсем не правильной работе условий if( _time - kbdTime() > interval ){ .. }
kbdTime имеет какой тип? Если отличный от uint8_t, то речь уже идёт о чём-то другом. Если всё-таки uint8_t, то проблема может упираться в undefined behaviour. Потому что если _time меньше, чем kbdTime(), то после вычитания получается отрицательная величина и при преобразовании её в unsigned (перед сравнением) возникает UB. Либо попросту переполнение, ибо небольшое отрицательное число, приведённое к беззнаковому типу есть большое число. Попробуй заменить

C
((_time - kbdTime(keyVal)) > KBD_TIME_BOUNCE)
на

C
tmp = kbdTime(keyVal); // перед if'ом
...
((_time >= tmp) && ((_time - tmp) > KBD_TIME_BOUNCE))
что-то поменяется?

Цитата Сообщение от Arhat109 Посмотреть сообщение
Им же было найдено решение по явному приведению типа промежуточного результата вычитания к беззнаковому байту
Формально тут тоже UB. А потому мог попасть пальцем в небо на предмет того, что конкретно в таком варианте компилятор построил именно так, как и хотелось

Добавлено через 2 минуты
Хотя... может быть тут есть такое свойство, что _time будет обязательно больше, чем kbdTime(keyVal). Но я не очень понимаю, как можно измерять время в миллисекундах и хранить его в одном байте. Это ведь надо быть уверенным, что между двумя событиями гарантированно меньше, чем 255 миллисекунд
1
20.02.2019, 22:10

Не по теме:

Цитата Сообщение от Evg Посмотреть сообщение
вопрос про корову, а ты отвечаешь про шарики
Ну, против таких аргументов - я пас! =-O :D

0
56 / 20 / 2
Регистрация: 18.06.2018
Сообщений: 199
21.02.2019, 06:53
Evg, Это "последняя" моя модификация его изделия .. играл тут со счетчиками цикла, решил проверить что раз знаковый поднимается до беззнакового, то может integer promotion остановится на "беззнаковом байте"? Похоже что так.

Для игнорирования дребезга 255мсек - более чем достаточно, но там дополнительно применен свой макрос kbdMillis(), который из unsigned long миллисекунд текущего тика вырезает даже не байт, а только 6 бит кратный по 64мсек (сдвиг вправо на 6 разрядов). Так что у него период опроса в 2U дает задержку для пропуска антидребезга в 128мсек, а период удержания в 63U это около 4 секунд! Как идея - мне очень понравилось.

Попробую ваш вариант, посмотрю что будет. В целом, как понял разных авторов на разных ресурсах, для AVR-GCC - явное приведение промежуточного результата - штатное решение по данной проблеме. К сожалению, сейчас и до конца февраля, проверить на Ардуино в реале не смогу .. нет под рукой и не будет. Смогу только посмотреть каков будет результат компиляции и как-то оценить его и только.

Добавлено через 8 минут
Да, kbdTime() как раз выделяет биты времени из кнопки и имеет uint8_t. Работа всех таких IF() основана как раз на беззнаковом вычитании, когда вычитая из меньшего времени большее начало мы получаем "заем" и .. правильный интервал в беззнаковых величинах. Тут как раз тот случай, когда нужно именно беззнаковое вычитание и integer promotion до знакового - есть грубая ошибка:

время 0..255 (uint8_t), интервал срабатывания 2U мсек. "Запуск таймера" произвели на 254 миллисекунде (тоже uint8_t) и?

1. 255 - 254 = 1, 1>=2U? : false;
2. 0 - 254 = 256(заем!) - 254 = 2, 2>=2U? : true

Вот только ради этого заема и нужны беззнаковые байтики
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21281 / 8305 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
21.02.2019, 15:13
Цитата Сообщение от Arhat109 Посмотреть сообщение
Тут как раз тот случай, когда нужно именно беззнаковое вычитание и integer promotion до знакового - есть грубая ошибка
Проблема тут не в promotion'е как таковом. Проблема в том, что из-за promotion'а по другому строятся преобразования типов, когда в выражении используются аргументы разных типов

C
#include <stdio.h>
 
int main (void)
{
  unsigned char a, b, c;
  a = 0;
  b = 254;
 
  /* Пример N1 */
  if ((a - b) > 5u)
    printf ("1: yes\n");
 
  /* Пример N2 */
  c = a - b;
  if (c > 5u)
    printf ("2: yes\n");
 
  return 0;
}
Code
$ gcc t.c
$ ./a.out 
1: yes
Тебе, судя по всему, нужно то, что в моём тесте используется в качестве примера N2. Чтобы визуально всё выглядело более понятным и не мозолило глаза

Я правильно понял, в чём твой вопрос?

Добавлено через 9 минут
В примерах N1 и N2 вычитание строится абсолютно одинаково. Правда во втором примере результат int'ового выражения преобразуется к unsigned char. Итоговое отличие идёт в операции сравнения. В примере N1 сравнение выглядит как (int > unsigned), а в примере N2 - (unsigned char > unsigned). В итоге пример N2 (с учётом преобразования типов при присваивании) отрабатывает как
((int преобразован к unsigned char, а он преобразован к unsigned) > unsigned)
а пример N1 как
((int преобразован к unsigned) > unsigned)

И именно в этом месте подгадил promotion (т.е. он повлиял на операцию сравнения)

Добавлено через 9 секунд
В примерах N1 и N2 вычитание строится абсолютно одинаково. Правда во втором примере результат int'ового выражения преобразуется к unsigned char. Итоговое отличие идёт в операции сравнения. В примере N1 сравнение выглядит как (int > unsigned), а в примере N2 - (unsigned char > unsigned). В итоге пример N2 (с учётом преобразования типов при присваивании) отрабатывает как
((int преобразован к unsigned char, а он преобразован к unsigned) > unsigned)
а пример N1 как
((int преобразован к unsigned) > unsigned)

И именно в этом месте подгадил promotion (т.е. он повлиял на операцию сравнения)
0
56 / 20 / 2
Регистрация: 18.06.2018
Сообщений: 199
21.02.2019, 15:54
Да, так поняли верно. У меня использована как-бы "скрытая переменная" поскольку вычитание - часть выражения. А у Вас в N2 оно вынесено явно и обратное преобразование к unsigned char компилятор обязан совершить явно.

Похоже что Вы правы не стоит городить сложные выражения с байтовыми данными и promotion должен "выпасть в осадок"... попробую.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
21.02.2019, 15:54

Как создать шаблон функции отдельно для знаковых и беззнаковых чисел
Имеется задача, создать шаблонную функцию для конвертации числа в строку. Но проблема в том, что при вызове функции sprintf и ей подобных...

Класс "Массив беззнаковых целых чисел"
Нужно создать класс содержащий динамический массив беззнаковых целых чисел.

Вычитание восьмеричных чисел
Добрый вечер. Нужно выполнить вычитание двух восьмеричных чисел. Сложение сделала: int octal_sum(int a,int b) { int sum=0,...

Вычитание двоичных чисел
Необходимо считать с текстового файла 2 двоичных числа, ограниченных 16 разрядом, выполнить вычитание в дополнительном коде, перевести в...

Вычитание двух отрицательных чисел
вычитание двух отрицательных чисел,помогите с кодом plz #include &lt;iostream&gt; #include &lt;conio.h&gt; #include &lt;string.h&gt; ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
30
Ответ Создать тему
Новые блоги и статьи
Алиса нашла кучу ошибок компиляции и запуска в проекте, который без проблем компилировался и запускался)))
anaschu 30.06.2026
Я пока посмеюся, но завтра проверю. А вообще интерсно. Дал алисе файл, в котором точно нет ошибок компиляции и запуска, и попросил их найти. Нашла кучу))) Критические ошибки, мешающие компиляции и. . .
сукцессия 16. Общий обзор, в основном что бы другие ии поняли
anaschu 29.06.2026
# Передаточный документ: модель микоризной сукцессии (для нового чата) Этот документ предназначен для того, чтобы новый чат Claude мог продолжить работу без необходимости заново разбираться в. . .
сукцессия 15 неявная схема
anaschu 29.06.2026
Алиса Калибровка параметров симбиотической модели: технический обзор Содержание: Введение Постановка проблемы Технические аспекты реализации Процесс внедрения изменений
сукцессия 14. Обновленная схема модели
anaschu 28.06.2026
ГЛОБАЛЬНАЯ ОПИСАТЕЛЬНАЯ СПЕЦИФИКАЦИЯ ЭКОСИСТЕМНОЙ МОДЕЛИ «SOIL CHEMISTRY & MYCORRHIZA 2. 0» https:/ / ibb. co/ NnkGpfMd Представленная интегрированная схема описывает непрерывную нелинейную. . .
сукцессия 13. Питон модель трехзонного мицелия, пока что в основном арбускулярного
anaschu 28.06.2026
## Разработка агентной модели микоризной сукцессии: от выявления артефактов к созданию комплексной системы ### Аннотация Представлено исследование по разработке агентной модели микоризной. . .
сукцессия 12. краткий список проверок модели перед запуском.
anaschu 27.06.2026
Скрытые отказы в моделях систем динамики (SD-models) экологических систем: два случая из практики Контекст Разбирался прототип модели систем динамики (SD-модели) микоризной сукцессии: пять. . .
Сукцессия 11. Проверка орудий перед войной: разработка через тестирование
anaschu 27.06.2026
Как не дать модели соврать самой себе: проверки для симуляции микоризной сукцессии Введение Когда вы строите математическую модель живой системы — грибов, растений, почвы — главная опасность. . .
10 сукцессия. Питон код войны грибов и растений
anaschu 27.06.2026
import numpy as np class PlantAgent: def __init__(self, name, strategy, initial_biomass): self. name = name self. strategy = strategy # "greedy" (широколиственные) или. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru