В последнее время стало появляться много сообщений об обнаружении в разных программах ошибок, связанных с целочисленным переполнением.
Допустим у нас есть код, где одна переменная время от времени увеличивается на некоторое заранее неизвестное значение. Например так: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. По крайней мере это характерно для платформы ИНТЕЛ)
Вопрос: как правильно отслеживать факт целочисленного переполнения?
Для 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;
}
>В последнее время стало появляться много сообщений об обнаружении в разных программах
>ошибок, связанных с целочисленным переполнением.
>Допустим у нас есть код, где одна переменная время от времени увеличивается
>на некоторое заранее неизвестное значение. Например так:
>
>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" );
>>В последнее время стало появляться много сообщений об обнаружении в разных программах
>>ошибок, связанных с целочисленным переполнением.
>>Допустим у нас есть код, где одна переменная время от времени увеличивается
>>на некоторое заранее неизвестное значение. Например так:
>>
>>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" );
>if ( counter > ~delta ) fprintf( stderr, "Overflow!!!\n" );Интересно и Красиво!
Большое вам спасибо. И этот метод вроде как должен переноситься на любые архитектуры.Интересно, а почему не сделали набор таких функций и не включили в стандартную библиотеку - вещь то очень нужная. Что-то типа этого:
int safe_add_uint32( uint32_t val1, uint32_t val2, uint32_t* result );
>Интересно, а почему не сделали набор таких функций и не включили в
>стандартную библиотеку - вещь то очень нужная. Что-то типа этого:
>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;
}
//-------------------------------------------------------------------
Вообще-то gcc действует строго по стандарту. Сложение для char и short выполняется в типе int.