itertools.groupby()
может свернуть соседние дубликаты, если вы готовы немного поработать.
print [x[0] for x in itertools.groupby([1, 2, 2, 3])]
Встроенный ассемблерный код позволяет Вам проверить, что переполнение укусило непосредственно. Если Вы собираетесь быть C++ использования, действительно необходимо изучить блок.
@MSalters: Хорошая идея.
, Если целочисленное вычисление требуется (для точности), но плавающая точка доступна, Вы могли сделать что-то как:
uint64_t foo( uint64_t a, uint64_t b ) {
double dc;
dc = pow( a, b );
if ( dc < UINT_MAX ) {
return ( powu64( a, b ) );
}
else {
// overflow
}
}
Очевидный способ, чтобы сделать это должен был бы переопределить все операторы (+ и * в особенности) и проверка на переполнение прежде perorming операции.
Вы не можете получить доступ к флагу переполнения от C/C++.
я не соглашаюсь с этим. Вы могли записать, что некоторые встраивают asm и используют jo
(переполнение перехода), инструкция, принимающая Вас, находится на x86 для захвата переполнения. Конечно, Вы, больше кодируете не было бы портативным к другой архитектуре.
взгляд info as
и info gcc
.
Вычислите результаты с удваивается. У них есть 15 значащих цифр. Ваше требование имеет трудную верхнюю границу на c 10 <глоток> 8 глоток> — это может иметь самое большее 8 цифр. Следовательно, результат будет точен, если это будет в диапазоне, и это не переполнится иначе.
Вы не можете получить доступ к флагу переполнения от C/C++.
Некоторые компиляторы позволяют Вам вставлять инструкции по прерыванию в код. На GCC опция является-ftrapv (но я должен признать, что никогда не использовал его. Проверит его после регистрации).
единственное портативное устройство и компилятор независимая вещь, которую можно сделать, состоит в том, чтобы проверить на переполнение самостоятельно. Точно так же, как Вы сделали в своем примере.
Редактирование:
Просто проверенный:-ftrapv, кажется, ничего не делает на x86 с помощью lastest GCC. Угадайте, что это - перенесенный от старой версии или характерный для некоторой другой архитектуры. Я ожидал, что компилятор вставит В код операции после каждого дополнения. К сожалению, это не делает этого.
Самый простой путь состоит в том, чтобы преобразовать Ваш unsigned long
с в unsigned long long
с, сделать Ваше умножение и сравнить результат с 0x100000000LL.
Вы, вероятно, найдете, что это более эффективно, чем выполнение подразделения, поскольку Вы сделали в своем примере.
, О, и это будет работать и в C и в C++ (поскольку Вы отметили вопрос с обоими).
<час> Просто взгляд на glibc руководство . Существует упоминание о прерывании целочисленного переполнения (FPE_INTOVF_TRAP
) как часть SIGFPE
. Это было бы идеально кроме противных битов в руководстве:
FPE_INTOVF_TRAP
Целочисленное переполнение (невозможный в программе C, если Вы не включаете захват переполнения определенным для аппаратных средств способом).
Определенный позор действительно.
Если у Вас есть тип данных, который больше, чем тот, который Вы хотите протестировать (скажите, что Вы делаете 32-разрядное добавляет, и у Вас есть 64-разрядный тип). Тогда это обнаружит, если переполнение произошло. Мой пример для 8-разрядного, добавляют. Но может быть увеличен.
uint8_t x, y; /* give these values */
const uint16_t data16 = x + y;
const bool carry = (data16 > 0xff);
const bool overflow = ((~(x ^ y)) & (x ^ data16) & 0x80);
Это основано на понятиях, объясненных на этой странице: http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/Comb/overflow.html
Для 32-разрядного примера, 0xff
становится 0xffffffff
, и 0x80
становится 0x80000000
, и наконец uint16_t
становится uint64_t
.
ПРИМЕЧАНИЕ : это ловит целочисленное переполнение дополнения/вычитания, и я понял, что Ваш вопрос включает умножение. В этом случае подразделение вероятно лучший подход. Это обычно - способ, которым calloc
реализации удостоверяются, что параметрические усилители не переполняются, поскольку они умножаются для получения заключительного размера.
Некоторые компиляторы обеспечивают доступ к флагу целочисленного переполнения в ЦП, который Вы могли тогда протестировать, но это не стандартно.
Вы могли также протестировать на возможность переполнения перед выполнением умножения:
if ( b > ULONG_MAX / a ) // a * b would overflow
Там способ определить, переполнится ли операция, вероятно, с помощью положений старшего значащего одного бита в операндах и небольшом основном знании двоичной математики.
Для дополнения, любые два операнда приведут к (самое большее) одному биту больше, чем самый большой операнд, самый высокий одноразрядный. Например:
bool addition_is_safe(uint32_t a, uint32_t b) {
size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
return (a_bits<32 && b_bits<32);
}
Для умножения, любые два операнда приведут к (самое большее) сумме битов операндов. Например:
bool multiplication_is_safe(uint32_t a, uint32_t b) {
size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
return (a_bits+b_bits<=32);
}
Точно так же можно оценить максимальный размер результата a
к питанию b
как это:
bool exponentiation_is_safe(uint32_t a, uint32_t b) {
size_t a_bits=highestOneBitPosition(a);
return (a_bits*b<=32);
}
(Заменяют числом битов для Вашего целевого целого числа, конечно.)
я не уверен в самом быстром способе определить положение самого высокого одноразрядного в числе, вот метод "в лоб":
size_t highestOneBitPosition(uint32_t a) {
size_t bits=0;
while (a!=0) {
++bits;
a>>=1;
};
return bits;
}
Это не прекрасно, но это даст Вам хорошую идею, могли ли какие-либо два числа переполниться, прежде чем Вы сделаете операцию. Я не знаю, было ли это быстрее, чем простая проверка результата путем, Вы предложили из-за цикла в эти highestOneBitPosition
функция, но это могло бы (особенно, если Вы знали, сколько битов было в операндах заранее).
Для целых чисел без знака просто проверьте, что результат меньше, чем один из аргументов:
unsigned int r, a, b;
r = a+b;
if (r < a)
{
// overflow
}
Для целых чисел со знаком можно проверить знаки аргументов и результата. целые числа различных знаков не могут переполниться, и целые числа того же знака переполняются, только результат, имеет другой знак:
signed int r, a, b, s;
r = a+b;
s = a>=0;
if (s == (b>=0) && s != (r>=0))
{
// overflow
}
Я вижу, вы используете целые числа без знака. По определению в C (я не знаю о C ++), беззнаковая арифметика не переполняется ... так что, по крайней мере, для C, ваша точка зрения спорна :)
С целыми числами со знаком, один раз произошло переполнение, неопределенное поведение (UB), и ваша программа может делать что угодно (например: тесты рендеринга безрезультатны).
#include <limits.h>
int a = <something>;
int x = <something>;
a += x; /* UB */
if (a < 0) { /* Unreliable test */
/* ... */
}
Чтобы создать соответствующую программу, необходимо протестировать переполнение до того, как сгенерирует указанное переполнение. Этот метод также можно использовать с целыми числами без знака:
// For addition
#include <limits.h>
int a = <something>;
int x = <something>;
if ((x > 0) && (a > INT_MAX - x)) /* `a + x` would overflow */;
if ((x < 0) && (a < INT_MIN - x)) /* `a + x` would underflow */;
// For subtraction
#include <limits.h>
int a = <something>;
int x = <something>;
if ((x < 0) && (a > INT_MAX + x)) /* `a - x` would overflow */;
if ((x > 0) && (a < INT_MIN + x)) /* `a - x` would underflow */;
// For multiplication
#include <limits.h>
int a = <something>;
int x = <something>;
// There may be a need to check for -1 for two's complement machines.
// If one number is -1 and another is INT_MIN, multiplying them we get abs(INT_MIN) which is 1 higher than INT_MAX
if ((a == -1) && (x == INT_MIN)) /* `a * x` can overflow */
if ((x == -1) && (a == INT_MIN)) /* `a * x` (or `a / x`) can overflow */
// general case
if (a > INT_MAX / x) /* `a * x` would overflow */;
if ((a < INT_MIN / x)) /* `a * x` would underflow */;
Для деления (за исключением особого случая INT_MIN
и -1
), нет никакой возможности перехода через ] INT_MIN
или INT_MAX
.
CERT разработал новый подход к обнаружению и сообщению о переполнении целых чисел со знаком, переносе целых чисел без знака и усечении целых чисел с использованием модели бесконечно ранжированных целых чисел «как если бы» (AIR). CERT опубликовал технический отчет с описанием модели и создал рабочий прототип на основе GCC 4.4.0 и GCC 4.5.0.
Целочисленная модель AIR либо выдает значение, эквивалентное тому, которое было бы получено с использованием бесконечно ранжированных целых чисел, либо приводит к нарушению ограничения времени выполнения. В отличие от предыдущих целочисленных моделей, целые числа AIR не требуют точных ловушек и, следовательно, не нарушают и не препятствуют большинству существующих оптимизаций.
Перехват целочисленных переполнений в C указывает на решение, более общее, чем то, которое обсуждается CERT (оно более общее с точки зрения обрабатываемых типов), даже если оно требует некоторых расширений GCC (я не знаю, насколько широко они поддерживаются).
Простой способ проверки на переполнение заключается в проверке того, меньше ли текущее значение предыдущего. Например, предположим, что у вас есть цикл для печати степени 2:
long lng;
int n;
for (n = 0; n < 34; ++n)
{
lng = pow (2, n);
printf ("%li\n", lng);
}
Добавление проверки на переполнение способом, который я описал, приводит к следующему:
long signed lng, lng_prev = 0;
int n;
for (n = 0; n < 34; ++n)
{
lng = pow (2, n);
if (lng <= lng_prev)
{
printf ("Overflow: %i\n", n);
/* Do whatever you do in the event of overflow. */
}
printf ("%li\n", lng);
lng_prev = lng;
}
Это работает для беззнаковых значений, а также для положительных и отрицательных знаковых значений.
Конечно, если вы хотите сделать что-то подобное для уменьшающихся значений вместо увеличивающихся, вы должны перевернуть знак <=
, чтобы сделать >=
, предполагая, что поведение при переполнении такое же, как и при переполнении. По правде говоря, это примерно то, чего вы можете добиться без доступа к флагу переполнения процессора (а для этого потребуется встроенный ассемблерный код, что в любом случае сделает ваш код непортабельным в разных реализациях).