Как обнаружить переполнение кратного числа без знака?

itertools.groupby() может свернуть соседние дубликаты, если вы готовы немного поработать.

print [x[0] for x in itertools.groupby([1, 2, 2, 3])]

577
задан Peter Mortensen 2 April 2019 в 22:48
поделиться

15 ответов

Встроенный ассемблерный код позволяет Вам проверить, что переполнение укусило непосредственно. Если Вы собираетесь быть C++ использования, действительно необходимо изучить блок.

-3
ответ дан Jonathan Allen 2 April 2019 в 22:48
поделиться

@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
    }
}
-2
ответ дан Frank Szczerba 2 April 2019 в 22:48
поделиться

Очевидный способ, чтобы сделать это должен был бы переопределить все операторы (+ и * в особенности) и проверка на переполнение прежде perorming операции.

-1
ответ дан Brian R. Bondy 2 April 2019 в 22:48
поделиться

Вы не можете получить доступ к флагу переполнения от C/C++.

я не соглашаюсь с этим. Вы могли записать, что некоторые встраивают asm и используют jo (переполнение перехода), инструкция, принимающая Вас, находится на x86 для захвата переполнения. Конечно, Вы, больше кодируете не было бы портативным к другой архитектуре.

взгляд info as и info gcc.

3
ответ дан Tarski 2 April 2019 в 22:48
поделиться

Вычислите результаты с удваивается. У них есть 15 значащих цифр. Ваше требование имеет трудную верхнюю границу на c 10 <глоток> 8   —   это может иметь самое большее 8 цифр. Следовательно, результат будет точен, если это будет в диапазоне, и это не переполнится иначе.

4
ответ дан SamB 2 April 2019 в 22:48
поделиться

Вы не можете получить доступ к флагу переполнения от C/C++.

Некоторые компиляторы позволяют Вам вставлять инструкции по прерыванию в код. На GCC опция является-ftrapv (но я должен признать, что никогда не использовал его. Проверит его после регистрации).

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

Редактирование:

Просто проверенный:-ftrapv, кажется, ничего не делает на x86 с помощью lastest GCC. Угадайте, что это - перенесенный от старой версии или характерный для некоторой другой архитектуры. Я ожидал, что компилятор вставит В код операции после каждого дополнения. К сожалению, это не делает этого.

13
ответ дан Nils Pipenbrinck 2 April 2019 в 22:48
поделиться

Самый простой путь состоит в том, чтобы преобразовать Ваш unsigned long с в unsigned long long с, сделать Ваше умножение и сравнить результат с 0x100000000LL.

Вы, вероятно, найдете, что это более эффективно, чем выполнение подразделения, поскольку Вы сделали в своем примере.

, О, и это будет работать и в C и в C++ (поскольку Вы отметили вопрос с обоими).

<час>

Просто взгляд на glibc руководство . Существует упоминание о прерывании целочисленного переполнения (FPE_INTOVF_TRAP) как часть SIGFPE. Это было бы идеально кроме противных битов в руководстве:

FPE_INTOVF_TRAP Целочисленное переполнение (невозможный в программе C, если Вы не включаете захват переполнения определенным для аппаратных средств способом).

Определенный позор действительно.

18
ответ дан Andrew Edgecombe 2 April 2019 в 22:48
поделиться

Если у Вас есть тип данных, который больше, чем тот, который Вы хотите протестировать (скажите, что Вы делаете 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 реализации удостоверяются, что параметрические усилители не переполняются, поскольку они умножаются для получения заключительного размера.

20
ответ дан Evan Teran 2 April 2019 в 22:48
поделиться

Некоторые компиляторы обеспечивают доступ к флагу целочисленного переполнения в ЦП, который Вы могли тогда протестировать, но это не стандартно.

Вы могли также протестировать на возможность переполнения перед выполнением умножения:

if ( b > ULONG_MAX / a ) // a * b would overflow
51
ответ дан Robert Gamble 2 April 2019 в 22:48
поделиться

Там способ определить, переполнится ли операция, вероятно, с помощью положений старшего значащего одного бита в операндах и небольшом основном знании двоичной математики.

Для дополнения, любые два операнда приведут к (самое большее) одному биту больше, чем самый большой операнд, самый высокий одноразрядный. Например:

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 функция, но это могло бы (особенно, если Вы знали, сколько битов было в операндах заранее).

163
ответ дан Head Geek 2 April 2019 в 22:48
поделиться

Для целых чисел без знака просто проверьте, что результат меньше, чем один из аргументов:

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
}
14
ответ дан Community 3 April 2019 в 08:48
поделиться

Я вижу, вы используете целые числа без знака. По определению в 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 .

209
ответ дан 22 November 2019 в 22:02
поделиться

CERT разработал новый подход к обнаружению и сообщению о переполнении целых чисел со знаком, переносе целых чисел без знака и усечении целых чисел с использованием модели бесконечно ранжированных целых чисел «как если бы» (AIR). CERT опубликовал технический отчет с описанием модели и создал рабочий прототип на основе GCC 4.4.0 и GCC 4.5.0.

Целочисленная модель AIR либо выдает значение, эквивалентное тому, которое было бы получено с использованием бесконечно ранжированных целых чисел, либо приводит к нарушению ограничения времени выполнения. В отличие от предыдущих целочисленных моделей, целые числа AIR не требуют точных ловушек и, следовательно, не нарушают и не препятствуют большинству существующих оптимизаций.

8
ответ дан 22 November 2019 в 22:02
поделиться

Перехват целочисленных переполнений в C указывает на решение, более общее, чем то, которое обсуждается CERT (оно более общее с точки зрения обрабатываемых типов), даже если оно требует некоторых расширений GCC (я не знаю, насколько широко они поддерживаются).

2
ответ дан 22 November 2019 в 22:02
поделиться

Простой способ проверки на переполнение заключается в проверке того, меньше ли текущее значение предыдущего. Например, предположим, что у вас есть цикл для печати степени 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;
}

Это работает для беззнаковых значений, а также для положительных и отрицательных знаковых значений.

Конечно, если вы хотите сделать что-то подобное для уменьшающихся значений вместо увеличивающихся, вы должны перевернуть знак <=, чтобы сделать >=, предполагая, что поведение при переполнении такое же, как и при переполнении. По правде говоря, это примерно то, чего вы можете добиться без доступа к флагу переполнения процессора (а для этого потребуется встроенный ассемблерный код, что в любом случае сделает ваш код непортабельным в разных реализациях).

-4
ответ дан 22 November 2019 в 22:02
поделиться