Как да проверите дали е настъпило препълване?

Възможен дубликат:
Най-добрият начин за откриване на целочислено препълване в C/C++

Това вероятно е въпрос за новобранец, но как мога да проверя, че някакво препълване е повлияло на стойността на моите числа в C. Например, когато умножавам цели числа и чакам целочислен резултат, ако действителният резултат е по-голям от максималната стойност на цяло число, действителният резултат е променено (нали?). И така, как мога да разбера дали се е случило нещо подобно?


person yasar    schedule 07.10.2011    source източник
comment
Предполагаемият дублиран въпрос е конкретно за стойности unsigned и отговорите отразяват това - докато този въпрос е по-общ, покриващ много по-сложния случай със знак.   -  person caf    schedule 09.10.2011


Отговори (7)


Препълването на цяло число със знак е като деление на нула - води до недефинирано поведение, така че трябва да проверите дали ще се случи преди да изпълните операцията с потенциално препълване. След като препълните, всички залози са изключени - вашият код може да направи всичко.

Макросите *_MAX и _MIN, дефинирани в <limits.h>, са полезни за това, но трябва да внимавате да не извиквате недефинирано поведение в самите тестове. Например, за да проверите дали a * b ще препълни дадено int a, b;, можете да използвате:

if ((b > 0 && a <= INT_MAX / b && a >= INT_MIN / b) ||
    (b == 0) ||
    (b == -1 && a >= -INT_MAX) ||
    (b < -1 && a >= INT_MAX / b && a <= INT_MIN / b))
{
    result = a * b;
}
else
{
    /* calculation would overflow */
}

(Имайте предвид, че един тънък капан, който това избягва, е, че не можете да изчислите INT_MIN / -1 - такова число не е гарантирано, че е представимо и наистина причинява фатален капан на общи платформи).

person caf    schedule 07.10.2011

Стандартът C99 има този раздел, обясняващ какво е недефинирано поведение:

3.4.3
недефинирано поведение
поведение, при използване на непреносима или грешна програмна конструкция или на грешни данни, за които този международен стандарт не налага изисквания ЗАБЕЛЕЖКА Възможно недефинирано поведение варира от пълно игнориране на ситуацията с непредсказуеми резултати, до поведение по време на превод или изпълнение на програма по документиран начин, характерен за средата (с или без издаване на диагностично съобщение), до прекратяване на превод или изпълнение (с издаване на диагностично съобщение).
ПРИМЕР
Пример за недефинирано поведение е поведението при препълване на цели числа.

Така че почти нямате късмет, няма преносим начин за откриване на това в общия случай, след факта.
Вашият компилатор/имплементация може да има разширения/поддръжка за него и има техники за избягване на това ситуации.
Вижте този въпрос за отличен съвет: Най-добрият начин за откриване целочислено препълване в C/C++.

person Mat    schedule 07.10.2011

Ако имате предвид, докато програмирате, можете да отстраните грешки в кода.

Ако имате предвид по време на изпълнение, можете да добавите някои условия, че ако надхвърли лимита, направете нещо.

C не знае какво да прави, когато доходността на изчислението е извън диапазона. Трябва да избегнете това чрез тестване на операнди.

person M. Suleiman    schedule 07.10.2011

Вижте това http://www.fefe.de/intof.html. Той ви показва как да проверите дали действителният резултат е по-голям от максималната стойност на цяло число.

person Sam Casil    schedule 07.10.2011

Ако полученото число е по-малко от един от входовете.

a + b = c, ако c ‹ a => препълване. редактиране: за бързо, това е само за събиране на цели числа без знак.

person Michel    schedule 07.10.2011

В общия случай не можете да знаете дали е настъпило препълване само като се взирате в резултата. Това, което можете да направите обаче, е да проверите дали операцията ще препълни отделно. напр. ако искате да проверите дали a*b препълва, където a и b са int's, трябва да решите неравенството

a * b <= INT_MAX

Тоест, ако a ‹= INT_MAX / b, тогава умножението ще препълни.

person janneb    schedule 07.10.2011
comment
Не забравяйте, че когато разделите двете страни на неравенството на b, трябва да обърнете неравенството, ако b е отрицателно. Също така не забравяйте за възможността за b == 0. - person caf; 07.10.2011

Докато правите аритметиката си в цели числа без знак или можете да разчитате на специфични за изпълнението гаранции за това как се държи препълването на цели числа със знак, можете да използвате различни трикове.

В случай на умножение без знак, най-простият е:

unsigned int lhs = something, rhs = something_else;
unsigned int product = lhs * rhs;

if (lhs != 0 && product/lhs != rhs) { overflow occurred }

Едва ли ще е бързо, но е преносимо. Проверката за неподписано препълване за добавяне също е доста проста - изберете един от операндите, след което препълването се е случило само ако сумата е по-малка от тази.

person Steve Jessop    schedule 07.10.2011