URL: https://www.opennet.me/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID9
Нить номер: 5154
[ Назад ]

Исходное сообщение
"Корректная проверка на целочисленное переполнение"

Отправлено dimus , 17-Фев-06 13:15 
В последнее время стало появляться много сообщений об обнаружении в разных программах ошибок, связанных с целочисленным переполнением.
Допустим у нас есть код, где одна переменная время от времени увеличивается на некоторое заранее неизвестное значение. Например так:

unsigned int counter;

void accounting( unsigned int delta )
{
   counter = counter + delta;
}

В определенный момент может быть достигнуто такое состояние, когда после прибавления очередной дельты у нас будет переполнение. Я придумал такой путь решения проблемы:
void accounting2( unsigned int delta )
{
   if( counter < (counter + delta) )
   {
      counter = counter + delta;
   }
   else
   {
      printf("Overflow detected\n");
   }
}

Однако этот способ аппаратно зависимый и не лишен недостатков: он не будет работать, если при переполнении не происходит циклического перехода в регистре. (т.е. допустим у нас есть восьмиразрядный регистр А, содержащий число 0xFE. Если добавить к нему 0x1, то мы получим 0xFF, а если добавить 0x3, то мы получим переполнение, и в регистре у нас будет 0x1. По крайней мере это характерно для платформы ИНТЕЛ)

Вопрос: как правильно отслеживать факт целочисленного переполнения?


Содержание

Сообщения в этом обсуждении
"Корректная проверка на целочисленное переполнение"
Отправлено СергейК , 17-Фев-06 15:32 
Для signed int подходит следующее

#define SAMESIGN(a,b)   (((a) < 0) == ((b) < 0))
void accounting2( int delta )
{
   int counter1 = counter + delta
   if (SAMESIGN(counter, delta) && !SAMESIGN(counter, counter1))
   {
      fprintf(stderr,"Overflow detected\n");
   }
   return counter1;
}


"Корректная проверка на целочисленное переполнение"
Отправлено sas , 18-Фев-06 11:44 
>В последнее время стало появляться много сообщений об обнаружении в разных программах
>ошибок, связанных с целочисленным переполнением.
>Допустим у нас есть код, где одна переменная время от времени увеличивается
>на некоторое заранее неизвестное значение. Например так:
>
>unsigned int counter;
>
>void accounting( unsigned int delta )
>{
>   counter = counter + delta;
>}
>
>В определенный момент может быть достигнуто такое состояние, когда после прибавления очередной
>дельты у нас будет переполнение. Я придумал такой путь решения проблемы:
>
>void accounting2( unsigned int delta )
>{
>   if( counter < (counter + delta) )
>   {
>      counter = counter + delta;
>   }
>   else
>   {
>      printf("Overflow detected\n");
>   }
>}
>
>Однако этот способ аппаратно зависимый и не лишен недостатков: он не будет
>работать, если при переполнении не происходит циклического перехода в регистре. (т.е.
>допустим у нас есть восьмиразрядный регистр А, содержащий число 0xFE. Если
>добавить к нему 0x1, то мы получим 0xFF, а если добавить
>0x3, то мы получим переполнение, и в регистре у нас будет
>0x1. По крайней мере это характерно для платформы ИНТЕЛ)
>
>Вопрос: как правильно отслеживать факт целочисленного переполнения?

if ( counter > ~delta )
   fprintf( stderr, "Overflow!!!\n" );


"Корректная проверка на целочисленное переполнение"
Отправлено sas , 18-Фев-06 11:47 
>>В последнее время стало появляться много сообщений об обнаружении в разных программах
>>ошибок, связанных с целочисленным переполнением.
>>Допустим у нас есть код, где одна переменная время от времени увеличивается
>>на некоторое заранее неизвестное значение. Например так:
>>
>>unsigned int counter;
>>
>>void accounting( unsigned int delta )
>>{
>>   counter = counter + delta;
>>}
>>
>>В определенный момент может быть достигнуто такое состояние, когда после прибавления очередной
>>дельты у нас будет переполнение. Я придумал такой путь решения проблемы:
>>
>>void accounting2( unsigned int delta )
>>{
>>   if( counter < (counter + delta) )
>>   {
>>      counter = counter + delta;
>>   }
>>   else
>>   {
>>      printf("Overflow detected\n");
>>   }
>>}
>>
>>Однако этот способ аппаратно зависимый и не лишен недостатков: он не будет
>>работать, если при переполнении не происходит циклического перехода в регистре. (т.е.
>>допустим у нас есть восьмиразрядный регистр А, содержащий число 0xFE. Если
>>добавить к нему 0x1, то мы получим 0xFF, а если добавить
>>0x3, то мы получим переполнение, и в регистре у нас будет
>>0x1. По крайней мере это характерно для платформы ИНТЕЛ)
>>
>>Вопрос: как правильно отслеживать факт целочисленного переполнения?
>
if ( counter > ~delta ) fprintf( stderr, "Overflow!!!\n" );

"Корректная проверка на целочисленное переполнение"
Отправлено dimus , 21-Фев-06 08:21 
>if ( counter > ~delta ) fprintf( stderr, "Overflow!!!\n" );

Интересно и Красиво!
Большое вам спасибо. И этот метод вроде как должен переноситься на любые архитектуры.

Интересно, а почему не сделали набор таких функций и не включили в стандартную библиотеку - вещь то очень нужная. Что-то типа этого:
int   safe_add_uint32( uint32_t val1, uint32_t val2, uint32_t* result );


"Корректная проверка на целочисленное переполнение"
Отправлено dimus , 22-Фев-06 15:07 
>Интересно, а почему не сделали набор таких функций и не включили в
>стандартную библиотеку - вещь то очень нужная. Что-то типа этого:
>int   safe_add_uint32( uint32_t val1, uint32_t val2, uint32_t* result );

Я решил сделать себе ряд таких функций. Для uint32_t и uint64_t все заработало прекрасно, и я ободренный этим, решил сделать также функции для uint8_t и uint16_t. Каково же было мое удивление и недоумение, когда эти функции незаработали!
Расследование выявило, что компилятор (gcc (GCC) 3.3.4) как-то странно рожает код для оператора if: вместо того, чтобы использовать cmpb или cmpw (для uint16_t), он использовал cmpl, и в результате функция не работает. Помогло приведение типов. Вот рабочий код:

//-------------------------------------------------------------------
// Безопасное сложение двух чисел с контролем переполнения
// val1 - первое число
// val2 - второе число
// res - указатель на переменную, в которую будет записан
// результат. В случае, если было зафиксированно переполнение,
// эта переменная останется в неизменном виде.
// Возвращаемое значение: 1 в случае успеха, 0 при переполнении
int     safe_add_uint8( uint8_t val1, uint8_t val2, uint8_t* res )
{
        int     result  = 0;

        if( (uint8_t)val1 <= (uint8_t)~val2 )
        {
                *res = val1 + val2;
                result = 1;
        }

        return  result;
}
//-------------------------------------------------------------------


"Корректная проверка на целочисленное переполнение"
Отправлено tilde , 22-Фев-06 15:45 
Вообще-то gcc действует строго по стандарту. Сложение для char и short выполняется в типе int.