Jump to content

    
Sign in to follow this  
MaxiMuz

Как в Си определить выход за пределы кода (переполнение) результата операции ?

Recommended Posts

Элементарная ситуация: Имеется два 16-битных числа , при их сложении нужно отслеживать ситуацию переполнения результата.

Пробую:

uint16_t temp16;
uint16_t sumADC;

if ((sumADC+=temp16)>0xffff)
     { // Отключение АЦП  !!
         ADCSRA=(1<<ADEN);}

понимаю что это нет то ....

Нужно как можно короче превести это к коду (для AVR):

add rd_l,rs_l
adc rd_h,rs_h
brcs m1
....

Как это все оформляется в Си ?

Edited by MaxiMuz

Share this post


Link to post
Share on other sites

Такой именно ассемблерный код как вы хотите может быть и не получится.

Варианты:

unit16_t x=sumADC;
sumADC+=temp16;
if (x>0xffffU-temp16)

unit16_t x=sumADC;
sumADC+=temp16;
if (((unit32_t)x+temp16)>0xffffUL)

unit32_t x=sumADC+temp16;
sumADC=(unit16_t)x;
if (x>0xffffUL)

Share this post


Link to post
Share on other sites

Можно попробовать так:

uint16_t temp16;
uint16_t sumADC;

sumADC+=temp16;
if (sumADC < temp16)
{
     { // Отключение АЦП  !!

Но ассемблерного варианта скорее всего тоже не получится.

Share this post


Link to post
Share on other sites
uint16_t temp16;
uint16_t sumADC;
uint32_t sum32;

sum32 = sum32 + temp16;

if ( sum32 <= 0xFFFFFFFF )
{
    sumADC = (uint16_t) sum32; 
}
else
{
   //overflow actions
}

ну, все-таки скорее всего чуть-чуть иначе:
uint16_t temp16;
uint16_t sumADC;
uint32_t sum32;

sum32 = sum32 + temp16;

if ( sum32 <= 0xFFFF ) // переполнение-то для 16-бит надо отслеживать
{
    sumADC = sum32; 
}
else
{
   //overflow actions
}

Share this post


Link to post
Share on other sites
uint32_t sum32;

В avr-gcc 4.7.x имеется тип __uint24

 

А что, arttab дал плохой совет? Доступ к флагам имеется?

Имеется. Просто обращение к SREG по маске

if (SREG & (1<<n)) {...}

Лучше всю эту красоту завернуть в макрос типа:

#define add16_sat(a,b) \
do \
{ \
   (a) += (b); \
   if (SREG & (1<<SREG_C)) \
   { \
      (a) = -1U; \
   } \
} while (0)

Share this post


Link to post
Share on other sites
Такой именно ассемблерный код как вы хотите может быть и не получится.

Варианты:

unit16_t x=sumADC;
sumADC+=temp16;
if (x>0xffffU-temp16)

unit16_t x=sumADC;
sumADC+=temp16;
if (((unit32_t)x+temp16)>0xffffUL)

unit32_t x=sumADC+temp16;
sumADC=(unit16_t)x;
if (x>0xffffUL)

введение дополнительных переменных , тем более 32 бита как предлагают Alexey K и ARV не приемлемо, тажкж на счету каждый такт. Т.к. процедура выполняется в прерывании и времяни на выполнение оч.мало

 

а по флагу переполнения не проще ли?

похоже что это самый лаконичный способ!

 

    if (SREG & (1<<SREG_C))
  80:    0f b6           in    r0, 0x3f; 63
  82:    00 fe           sbrs    r0, 0
  84:    04 c0           rjmp    .+8      ; 0x8e <__vector_9+0x48>

конечно не "brcc m1" ...

Share this post


Link to post
Share on other sites
введение дополнительных переменных , тем более 32 бита как предлагают Alexey K и ARV не приемлемо, тажкж на счету каждый такт. Т.к. процедура выполняется в прерывании и времяни на выполнение оч.мало

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

 

похоже что это самый лаконичный способ!

    if (SREG & (1<<SREG_C))

Он не гаранитированно даёт результат. У вас этот фрагмент кода сейчас такой:

uint16_t temp16;
uint16_t sumADC;
sumADC+=temp16;
if (SREG & (1<<SREG_C))

Допустим что есть ещё некоторые переменные, с которыми до того или после того производятся действия, и фрагмент большего размера может быть например что-то вроде:

uint16_t x,y;
...
x+=y;
...
uint16_t temp16;
uint16_t sumADC;
sumADC+=temp16;
if (SREG & (1<<SREG_C))

Компилятор имеет право переставить действия, и иногда он этим правом пользуется, и может получиться в ассемблере уже что-то будто компилированное с такого текста:

uint16_t x,y;
...
...
uint16_t temp16;
uint16_t sumADC;
sumADC+=temp16;
x+=y;
if (SREG & (1<<SREG_C))

и результат будет неверным.

Share this post


Link to post
Share on other sites
Проверьте что в ассемблере получится. Может быть соптимизируется и нормально будет, дополнительные переменные могут быть выкинуты компилятором.

 

Он не гаранитированно даёт результат. У вас этот фрагмент кода сейчас такой:

uint16_t temp16;
uint16_t sumADC;
sumADC+=temp16;
if (SREG & (1<<SREG_C))

Допустим что есть ещё некоторые переменные, с которыми до того или после того производятся действия, и фрагмент большего размера может быть например что-то вроде:

uint16_t x,y;
...
x+=y;
...
uint16_t temp16;
uint16_t sumADC;
sumADC+=temp16;
if (SREG & (1<<SREG_C))

Компилятор имеет право переставить действия, и иногда он этим правом пользуется, и может получиться в ассемблере уже что-то будто компилированное с такого текста:

uint16_t x,y;
...
...
uint16_t temp16;
uint16_t sumADC;
sumADC+=temp16;
x+=y;
if (SREG & (1<<SREG_C))

и результат будет неверным.

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

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this