Почему поймал переполнение?

RegNo
Offline
Зарегистрирован: 21.06.2017

Вычисляю выражение  L=L+New-Old-1;  Если не использовать промежуточную переменную delta=New-Old-1;  L=L+delta;  то возникает переполнение .

Отрицательных чисел в явном виде не создаю, New приходит ко мне строго как беззнаковое uint32_t  и всегда больше Old  хотя на 1 или на 2.  Подозреваю компилятор изменил порядок выполнения формулы и  начал с вычитания ?  Значение New в первом цикле =0,  а после вычитания 1 переходит что-ли в -1, те 0xFF FF FF FF ?

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

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

"Правильный"

uint32_t PN_New=0;
uint32_t PN_Old=0, Qty_Lost=0;
uint32_t delta;
int CountW=0;

void setup(void) 
{Serial.begin(115200); }

void loop(void)
{
      PN_New++; // номер текущ цикла
     delta=PN_New-PN_Old-1;
     PN_Old=PN_New; // номер предыдущего цикла
     Qty_Lost=Qty_Lost+delta;    
     //Qty_Lost=Qty_Lost+PN_New-PN_Old-1; //
         
     if(CountW ++ >5) {CountW=0;  PN_New++; } 
     Serial.print("QTY "); Serial.println(Qty_Lost);
}
     
/*
QTY 0
QTY 0
QTY 0
QTY 0
QTY 0
QTY 0
QTY 0
QTY 1
QTY 1
QTY 1
QTY 1
QTY 1
QTY 1
QTY 1
QTY 2
QTY 2
QTY 2
QTY 2
QTY 2
QTY 2
QTY 2
QTY 3
QTY 3
QTY 3
QTY 3
QTY 3
QTY 3
QTY 3
QTY 4
QTY 4
QTY 4
*/

"Неправильный"

uint32_t PN_New=0;
uint32_t PN_Old=0, Qty_Lost=0;
uint32_t delta;
int CountW=0;
void setup(void) 
{Serial.begin(115200); }
void loop(void)
{
      PN_New++; // номер текущ 
     //delta=PN_New-PN_Old-1;
     PN_Old=PN_New; // номер предыдущего 
     // Qty_Lost=Qty_Lost+delta    
     Qty_Lost=Qty_Lost+PN_New-PN_Old-1; //
          
     if(CountW ++ >10) {CountW=0;  PN_New++; } 
      Serial.print("QTY "); Serial.println(Qty_Lost);
}
     
/*
QTY 4294967295
QTY 4294967294
QTY 4294967293
QTY 4294967292
QTY 4294967291
QTY 4294967290
QTY 4294967289
QTY 4294967288
QTY 4294967287
QTY 4294967286
QTY 4294967285
QTY 4294967284
QTY 4294967283
QTY 4294967282
QTY 4294967281
QTY 4294967280
QTY 4294967279
QTY 4294967278
QTY 4294967277
QTY 4294967276
QTY 4294967275
*/

"Скобки не защитили от переполнения: "

uint32_t PN_New=0;
uint32_t PN_Old=0, Qty_Lost=0;
uint32_t delta;
int CountW=0;

void setup(void) 
{Serial.begin(115200);}

void loop(void)
{
      PN_New++; // номер текущ 
     //delta=PN_New-PN_Old-1;
     PN_Old=PN_New; // номер предыдущего 
     // Qty_Lost=Qty_Lost+delta    
     Qty_Lost=(Qty_Lost+(PN_New-PN_Old)) -1; // Изменен порядок вычисления
     if(CountW ++ >10) {CountW=0;  PN_New++; } 
     Serial.print("QTY "); Serial.println(Qty_Lost);
}

 

 

RegNo
Offline
Зарегистрирован: 21.06.2017

PS: даже такое явное определение порядка  не помогает:

     Qty_Lost+=PN_New; // Порядок выполнения
     Qty_Lost-=PN_Old; // Порядок выполнения
     Qty_Lost--; // Порядок выполнения

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

RegNo пишет:

Подозреваю компилятор изменил порядок выполнения формулы и  начал с вычитания ?  

Не мог он такого сделать. Можете посмотреть ассемблерный код и убедиться в этом.

b707
Offline
Зарегистрирован: 26.05.2017

RegNo пишет:

PS: даже такое явное определение порядка  не помогает:

     Qty_Lost+=PN_New; // Порядок выполнения
     Qty_Lost-=PN_Old; // Порядок выполнения
     Qty_Lost--; // Порядок выполнения

 

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Кто Вам сказал, что Ваши "правильный" и неправильный" коды идентичны? Ни грамма не так. Сравните:

//
// Типа правильный  
//
PN_New++; // номер текущ цикла
delta = PN_New - PN_Old - 1;
PN_Old = PN_New; // номер предыдущего цикла
Qty_Lost = Qty_Lost + delta;

//
// Типа неправильный  
//
PN_New++; // номер текущ
PN_Old = PN_New; // номер предыдущего
Qty_Lost = Qty_Lost + PN_New - PN_Old - 1; //

Вот и смотрите. Самый первый проход, когда PN_New и PN_Old оба равны 0.

В верхнем коде перед последней строкой delta равна 0
В нижнем коде перед последней строкой "PN_New - PN_Old - 1" равно -1 (с точностью до беззнаковости, конечно)

Т.е. у Вас просто два разных кода, которые работают именно так, как написаны и выдают разные результаты.

RegNo
Offline
Зарегистрирован: 21.06.2017

ЕвгенийП пишет:
Кто Вам сказал, что Ваши "правильный" и неправильный" коды идентичны? Ни грамма не так. Сравните:

Вот и смотрите. Самый первый проход, когда PN_New и PN_Old оба равны 0.

В верхнем коде перед последней строкой delta равна 0
В нижнем коде перед последней строкой "PN_New - PN_Old - 1" равно -1 (с точностью до беззнаковости, конечно)

Т.е. у Вас просто два разных кода, которые работают именно так, как написаны и выдают разные результаты.

Понимаю, что чудес не бывает, но не могу понять в чем моя ошибка и неидентичность кодов (кроме возможного переполнения из-за вычитания 1 из нуля? ).

Наверное на меня напал "тупизм", но код с первого взгляда  для меня эквивалентен. 

В верхнем фрагменте отдельно вычисляется delta=New+Old-1;  (строка 05 в цитате Вашего кода) и вторым действием суммируется с Qty=Qty+delta; В нижнем фрагменте (строка 14) сразу же вычисляется  Qty=Qty+New+Old-1;   Присваивания Old=New; имхо на суть дела не влияют и переполнение возникает уже в первом проходе цикла.

Прошу Вас не обижаться на меня, но пока не могу понять суть моей ошибки.

//
// Типа правильный  
//
PN_New++; // номер текущ цикла
delta = PN_New - PN_Old - 1;
PN_Old = PN_New; // номер предыдущего цикла
Qty_Lost = Qty_Lost + delta;

//
// Типа неправильный  
//
PN_New++; // номер текущ
PN_Old = PN_New; // номер предыдущего
Qty_Lost = Qty_Lost + PN_New - PN_Old - 1; //

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

RegNo пишет:

Наверное на меня напал "тупизм"

Похоже на то.

Давайте, так (я своему внуку так всегда говорю). Печатаешь текст на бумаге, ставишь палец на первую строчку и идёшь по коду, записывая на полях всё, что происходит. Поехали:

RegNo пишет:

//
// Типа правильный  
//
PN_New++; // номер текущ цикла
delta = PN_New - PN_Old - 1;
PN_Old = PN_New; // номер предыдущего цикла
Qty_Lost = Qty_Lost + delta;

//
// Типа неправильный  
//
PN_New++; // номер текущ
PN_Old = PN_New; // номер предыдущего
Qty_Lost = Qty_Lost + PN_New - PN_Old - 1; //

Изначально оба PN_New и PN_Old равны 0.

1. В строке 4 PN_New стало 1
2. В строке 5 delta стала равно 0 (1-0-1=0)
3. В строке 7 Qty_Lost стала равна 0 (0+0=0)
 
Теперь второй код
1. В строке 12 PN_New стала равна 1
2. В строке 13 PN_Old стала равна 1
3. в строке 14 Qty_Lost стала равна -1 (0+1-1-1=-1)
 
Вы по-прежнему утверждаете, что коды эквивалентны? 
RegNo
Offline
Зарегистрирован: 21.06.2017

ЕвгенийП пишет:
Изначально оба PN_New и PN_Old равны 0.

1. В строке 4 PN_New стало 1
2. В строке 5 delta стала равно 0 (1-0-1=0)
3. В строке 7 Qty_Lost стала равна 0 (0+0=0)
 
Теперь второй код
1. В строке 12 PN_New стала равна 1
2. В строке 13 PN_Old стала равна 1
3. в строке 14 Qty_Lost стала равна -1 (0+1-1-1=-1)
 
Вы по-прежнему утверждаете, что коды эквивалентны?
 
ЕвгенийП,  благодаря Вам понял мою ошибку.  Она была в преждевременном  присвоении  PN_Old=PN_New.  Для того, чтобы второй код был эквивалентен первому, присвоение  должно быть после вычисления Qty_Lost.
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Незачто. Успехов!