¿Cómo detecto el desbordamiento de multiplicación de enteros sin signo?

618

Estaba escribiendo un programa en C ++ para encontrar todas las soluciones de una b = c , donde un , b y c juntos utilizan todos los dígitos 0-9 exactamente una vez. El programa enlazadas sobre los valores de una y B , y se pasó una rutina de conteo de dígitos cada vez en una , b y un b para comprobar si la condición dígitos se mostró satisfecho.

Sin embargo, se pueden generar soluciones espurias cuando una b desborda el límite entero. Terminé buscando esto usando un código como:

unsigned long b, c, c_test;
...
c_test=c*b;         // Possible overflow
if (c_test/b != c) {/* There has been an overflow*/}
else c=c_test;      // No overflow

¿Hay una mejor manera de probar el desbordamiento? Sé que algunos chips tienen un indicador interno que se establece cuando se produce un desbordamiento, pero nunca lo he visto a través de C o C ++.


Tenga en cuenta que el desbordamiento firmado int es un comportamiento indefinido en C y C ++ y, por lo tanto, debe detectarlo sin causarlo. Para el desbordamiento int firmado antes de la adición, consulte Detección del desbordamiento firmado en C / C ++ .

Chris Johnson
fuente
21
Información que puede ser útil sobre este tema: Capítulo 5 de "Codificación segura en C y C ++" de Seacord - http://www.informit.com/content/images/0321335724/samplechapter/seacord_ch05.pdf Clases SafeInt para C ++ - http : //blogs.msdn.com/david_leblanc/archive/2008/09/30/safeint-3-on-codeplex.aspx - http://www.codeplex.com/SafeInt Biblioteca IntSafe para C: - [ blogs.msdn .com / michael_howard / archiv
Michael Burr
3
La codificación segura de Seacord es un gran recurso, pero no use IntegerLib. Ver blog.regehr.org/archives/593 .
jww
32
La opción del compilador gcc -ftrapvhará que genere un SIGABRT en el desbordamiento de entero (firmado). Ver aquí .
nibot
1
No responde la pregunta de desbordamiento, pero otra forma de abordar el problema sería usar una biblioteca BigNum como GMP para garantizar que siempre tenga suficiente precisión. No tendrá que preocuparse por el desbordamiento si asigna suficientes dígitos por adelantado.
wrdieter
1
La información dada por @HeadGeek en su respuesta es más o menos lo que yo diría también. Sin embargo, con una adición. La forma en que está detectando el sobrevuelo de una multiplicación ahora es probablemente la más rápida. En ARM, como he comentado en la respuesta de HeadGeek, puede usar la clzinstrucción o la __clz(unsigned)función para determinar el rango del número (donde está su bit más alto). Como no estoy seguro de si esto está disponible en x86 o x64, asumiré que no lo es y diré que encontrar el bit más significativo requerirá las peores log(sizeof(int)*8)instrucciones.
sin tontos

Respuestas:

229

Veo que estás usando enteros sin signo. Por definición, en C (no sé sobre C ++), la aritmética sin signo no se desborda ... por lo tanto, al menos para C, su punto es discutible :)

Con enteros con signo, una vez que se ha desbordado, se ha producido un comportamiento indefinido (UB) y su programa puede hacer cualquier cosa (por ejemplo: hacer que las pruebas no sean concluyentes). 

#include <limits.h>

int a = <something>;
int x = <something>;
a += x;              /* UB */
if (a < 0) {         /* Unreliable test */
  /* ... */
}

Para crear un programa conforme, debe probar el desbordamiento antes de generar dicho desbordamiento. El método también se puede usar con enteros sin signo:

// 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 */;

Para la división (excepto para el INT_MINy -1caso especial), no hay ninguna posibilidad de pasar por encima INT_MINo INT_MAX.

pmg
fuente
97
Los enteros sin signo tampoco se desbordan estrictamente en C ++ (ISO / IEC 14882: 2003 3.9.1.4). Mi uso de 'desbordamiento' en la pregunta fue el significado más coloquial, destinado a incluir la envoltura bien definida de tipos sin signo, ya que estaba interesado en ints sin signo que representan enteros positivos matemáticos, no enteros positivos mod 2 ^ 32 (o 2 ^ 64) La distinción entre el desbordamiento como una desviación del comportamiento entero matemático de tamaño infinito y el desbordamiento como un comportamiento indefinido en el lenguaje rara vez se hace explícito.
Chris Johnson el
15
Esa prueba no necesita ser x >= 0- x > 0será suficiente (si x == 0, entonces x + ano puede desbordarse por razones obvias).
caf
2
@pmg, ¿hay una cita de apoyo del estándar?
Pacerier
55
Me gusta este enfoque ... Sin embargo, tenga cuidado: la detección de desbordamiento de multiplicación supone una x positiva. Para x == 0, conduce a dividir por detección de cero, y para x negativo, siempre detecta erróneamente el desbordamiento.
Franz D.
44
if ((a < INT_MIN / x))la prueba es demasiado tarde Se if (x == -1) necesita una prueba primero.
chux - Restablecer Monica
164

No es una forma de determinar si una operación es probable que rebose, utilizando las posiciones de los uno-bits más significativos de los operandos y un poco de conocimiento básico binario-matemáticas.

Además, cualquiera de los dos operandos dará como resultado (como máximo) un bit más que el bit más alto del operando más grande. Por ejemplo:

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);
}

Para la multiplicación, cualquiera de los dos operandos dará como resultado (como máximo) la suma de los bits de los operandos. Por ejemplo:

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);
}

Del mismo modo, puede estimar el tamaño máximo del resultado aa la potencia de besta manera:

bool exponentiation_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a);
    return (a_bits*b<=32);
}

(Sustituya el número de bits para su entero objetivo, por supuesto).

No estoy seguro de la forma más rápida de determinar la posición del bit más alto en un número, aquí hay un método de fuerza bruta:

size_t highestOneBitPosition(uint32_t a) {
    size_t bits=0;
    while (a!=0) {
        ++bits;
        a>>=1;
    };
    return bits;
}

No es perfecto, pero eso le dará una buena idea de si dos números podrían desbordarse antes de realizar la operación. No sé si sería más rápido que simplemente verificar el resultado de la manera que sugirió, debido al bucle en la highestOneBitPositionfunción, pero podría (especialmente si sabía de antemano cuántos bits había en los operandos).

Head Geek
fuente
98
y, por supuesto, puede cambiar el nombre del más alto de OneBitPosition para iniciar sesión :)
Oliver Hallam
37
Sí, es la misma operación que log2, pero eso no sería necesariamente tan obvio para alguien que no tenía una base matemática.
Head Geek
48
¿No subestima este algoritmo las respuestas seguras? 2 ^ 31 + 0 se detectaría como inseguro ya que el valor más alto de OneBitPosition (2 ^ 31) = 32. (2 ^ 32 - 1) * 1 se detectaría como inseguro ya que 32 + 1> 32. 1 ^ 100 se detectaría como inseguro desde 1 * 100 > 32.
clahey
19
de acuerdo con su multiplication_is_safe 0x8000 * 0x10000desbordamiento (las posiciones de bits son 16 + 17 = 33, que es > 32 ), aunque no lo hace porque 0x8000 * 0x10000 = 0x80000000obviamente todavía cabe en un int de 32 bits sin signo. Este es solo uno de los ejemplos de mayo para los que este código no funciona. 0x8000 * 0x10001, ...
Michi
13
@GT_mh: ¿Tu punto? Como dije, no es perfecto; es una regla general que definitivamente dirá cuándo algo es seguro, pero no hay forma de determinar si cada cálculo estaría bien sin hacer el cálculo completo. 0x8000 * 0x10000no es "seguro", según esta definición, aunque resulte estar bien.
Head Geek
148

Clang 3.4+ y GCC 5+ ofrecen funciones aritméticas comprobadas. Ofrecen una solución muy rápida a este problema, especialmente en comparación con las comprobaciones de seguridad de prueba de bits.

Para el ejemplo en la pregunta de OP, funcionaría así:

unsigned long b, c, c_test;
if (__builtin_umull_overflow(b, c, &c_test))
{
    // Returned non-zero: there has been an overflow
}
else
{
    // Return zero: there hasn't been an overflow
}

La documentación de Clang no especifica si c_testcontiene el resultado desbordado si ocurrió un desbordamiento, pero la documentación de GCC dice que sí. Dado que a estos dos les gusta ser __builtincompatibles, probablemente sea seguro asumir que así es como funciona Clang.

Hay una __builtinpara cada operación aritmética que puede desbordarse (suma, resta, multiplicación), con variantes con signo y sin signo, para tamaños int, tamaños largos y tamaños largos largos. La sintaxis para el nombre es __builtin_[us](operation)(l?l?)_overflow:

  • upara no firmado o spara firmado ;
  • la operación es uno de add, subo mul;
  • sin lsufijo significa que los operandos son ints; uno lmedio long; dos ls significan long long.

Entonces, para una suma de enteros largos marcados y marcados, sería __builtin_saddl_overflow. La lista completa se puede encontrar en la página de documentación de Clang .

GCC 5+ y Clang 3,8+ ofrecen, además, órdenes internas genéricos que funcionan sin especificar el tipo de los valores: __builtin_add_overflow, __builtin_sub_overflowy __builtin_mul_overflow. Estos también funcionan en tipos más pequeños que int.

Los componentes se reducen a lo que es mejor para la plataforma. En x86, verifican el acarreo, desbordamiento y firman banderas.

El cl.exe de Visual Studio no tiene equivalentes directos. Para sumas y restas sin firmar, incluso <intrin.h>le permitirá usar addcarry_uNNy subborrow_uNN(donde NN es el número de bits, como addcarry_u8o subborrow_u64). Su firma es un poco obtusa:

unsigned char _addcarry_u32(unsigned char c_in, unsigned int src1, unsigned int src2, unsigned int *sum);
unsigned char _subborrow_u32(unsigned char b_in, unsigned int src1, unsigned int src2, unsigned int *diff);

c_in/ b_ines el indicador de acarreo / préstamo en la entrada y el valor de retorno es el transporte / préstamo en la salida No parece tener equivalentes para operaciones con signo o multiplicaciones.

De lo contrario, Clang para Windows ahora está listo para la producción (lo suficientemente bueno para Chrome), por lo que también podría ser una opción.

zneak
fuente
__builtin_sub_overflowdefinitivamente no está en Clang 3.4.
Richard Cook, el
2
@ RichardCook, tomó algo de tiempo, pero Clang tiene los genéricos incorporados a partir de la versión 3.9.
zneak
@tambre, no creo que haya.
zneak
44
Según los documentos , __builtin_add_overflowy los amigos ya deberían estar disponibles en Clang 3.8.
Lekensteyn
2
Gracias. Esto funciona muy bien. ¿Alguna idea de cuál es la función correspondiente para Visual C ++? Parece que no puedo encontrarlos.
Mudit Jain
53

Algunos compiladores proporcionan acceso al indicador de desbordamiento de enteros en la CPU que luego puede probar, pero esto no es estándar.

También puede probar la posibilidad de desbordamiento antes de realizar la multiplicación:

if ( b > ULONG_MAX / a ) // a * b would overflow
Robert Gamble
fuente
11
... o use numeric_limits <TYPE> :: max ()
Jonas Gulle
20
No olvides manejar a = 0 - la división se divide entonces.
Thelema el
16
@Thelema: "No olvides manejar a = 0" - e INT_MIN / -1.
jww
1
¿Y si b == ULONG_MAX / a? Entonces todavía puede encajar, dado que se adivide ULONG_MAXsin residuos.
los cerdos
Es curioso que, en cuanto al rendimiento, una multiplicación sea bastante rápida en comparación con una división y que esté agregando una división para cada multiplicación. Esto no suena como la solución.
DrumM
40

Advertencia: GCC puede optimizar una comprobación de desbordamiento al compilar con -O2. La opción -Wallle dará una advertencia en algunos casos como

if (a + b < a) { /* Deal with overflow */ }

pero no en este ejemplo:

b = abs(a);
if (b < 0) { /* Deal with overflow */ }

La única forma segura es verificar el desbordamiento antes de que ocurra, como se describe en el documento del CERT , y esto sería increíblemente tedioso de usar sistemáticamente.

Compilar con -fwrapvresuelve el problema, pero deshabilita algunas optimizaciones.

Necesitamos desesperadamente una mejor solución. Creo que el compilador debería emitir una advertencia por defecto al hacer una optimización que se basa en que no se produzca un desbordamiento. La situación actual le permite al compilador optimizar una verificación de desbordamiento, lo cual es inaceptable en mi opinión.

Una niebla
fuente
8
Tenga en cuenta que los compiladores sólo pueden hacerlo con firmados tipos enteros; el desbordamiento está completamente definido para los tipos enteros sin signo. Aún así, sí, ¡es una trampa bastante peligrosa!
SamB
1
"Creo que el compilador debería emitir una advertencia de forma predeterminada al realizar una optimización que se basa en que no se produzca un desbordamiento". - Entonces, ¿ for(int k = 0; k < 5; k++) {...}debería hacer una advertencia?
user253751
2
@immibis: ¿Por qué debería hacerlo? Los valores de kse pueden determinar fácilmente en tiempo de compilación. El compilador no tiene que hacer ninguna suposición.
MikeMB
2
@immibis: Para citar lo anterior: "Creo que el compilador debería emitir una advertencia por defecto al hacer una optimización que se basa en que no se produzca un desbordamiento".
MikeMB
1
@MikeMB La optimización en la que el compilador no se molesta en comprobar que nes inferior a 32, antes de emitir una instrucción de cambio que solo utiliza los 5 bits inferiores de n?
user253751
30

Clang ahora admite comprobaciones dinámicas de desbordamiento para enteros con y sin signo. Consulte el modificador -fsanitize = integer . Por ahora, es el único compilador de C ++ con comprobación de desbordamiento dinámico totalmente compatible con fines de depuración.

ZAB
fuente
26

Veo que muchas personas respondieron la pregunta sobre el desbordamiento, pero quería abordar su problema original. Dijo que el problema era encontrar a b = c de modo que todos los dígitos se usen sin repetir. Ok, eso no es lo que preguntó en esta publicación, pero sigo pensando que era necesario estudiar el límite superior del problema y concluir que nunca necesitaría calcular o detectar un desbordamiento (nota: no soy competente) en matemáticas, así que hice esto paso a paso, pero el resultado final fue tan simple que podría tener una fórmula simple).

El punto principal es que el límite superior que requiere el problema para a, b o c es 98.765.432. De todos modos, comenzando dividiendo el problema en las partes triviales y no triviales:

  • x 0 == 1 (todas las permutaciones de 9, 8, 7, 6, 5, 4, 3, 2 son soluciones)
  • x 1 == x (no hay solución posible)
  • 0 b == 0 (no hay solución posible)
  • 1 b == 1 (no hay solución posible)
  • a b , a> 1, b> 1 (no trivial)

Ahora solo tenemos que mostrar que no hay otra solución posible y que solo las permutaciones son válidas (y luego el código para imprimirlas es trivial). Volvemos al límite superior. En realidad, el límite superior es c ≤ 98.765.432. Es el límite superior porque es el número más grande con 8 dígitos (10 dígitos en total menos 1 para cada ayb). Este límite superior es solo para c porque los límites para ayb deben ser mucho más bajos debido al crecimiento exponencial, como podemos calcular, variando b de 2 al límite superior:

    9938.08^2 == 98765432
    462.241^3 == 98765432
    99.6899^4 == 98765432
    39.7119^5 == 98765432
    21.4998^6 == 98765432
    13.8703^7 == 98765432
    9.98448^8 == 98765432
    7.73196^9 == 98765432
    6.30174^10 == 98765432
    5.33068^11 == 98765432
    4.63679^12 == 98765432
    4.12069^13 == 98765432
    3.72429^14 == 98765432
    3.41172^15 == 98765432
    3.15982^16 == 98765432
    2.95305^17 == 98765432
    2.78064^18 == 98765432
    2.63493^19 == 98765432
    2.51033^20 == 98765432
    2.40268^21 == 98765432
    2.30883^22 == 98765432
    2.22634^23 == 98765432
    2.15332^24 == 98765432
    2.08826^25 == 98765432
    2.02995^26 == 98765432
    1.97741^27 == 98765432

Observe, por ejemplo, la última línea: dice que 1.97 ^ 27 ~ 98M. Entonces, por ejemplo, 1 ^ 27 == 1 y 2 ^ 27 == 134.217.728 y eso no es una solución porque tiene 9 dígitos (2> 1.97, por lo que en realidad es más grande de lo que debería probarse). Como se puede ver, las combinaciones disponibles para probar ayb son realmente pequeñas. Para b == 14, necesitamos probar 2 y 3. Para b == 3, comenzamos en 2 y terminamos en 462. Todos los resultados se otorgan a menos de ~ 98M.

Ahora solo pruebe todas las combinaciones anteriores y busque las que no repitan ningún dígito:

    ['0', '2', '4', '5', '6', '7', '8'] 84^2 = 7056
    ['1', '2', '3', '4', '5', '8', '9'] 59^2 = 3481
    ['0', '1', '2', '3', '4', '5', '8', '9'] 59^2 = 3481 (+leading zero)
    ['1', '2', '3', '5', '8'] 8^3 = 512
    ['0', '1', '2', '3', '5', '8'] 8^3 = 512 (+leading zero)
    ['1', '2', '4', '6'] 4^2 = 16
    ['0', '1', '2', '4', '6'] 4^2 = 16 (+leading zero)
    ['1', '2', '4', '6'] 2^4 = 16
    ['0', '1', '2', '4', '6'] 2^4 = 16 (+leading zero)
    ['1', '2', '8', '9'] 9^2 = 81
    ['0', '1', '2', '8', '9'] 9^2 = 81 (+leading zero)
    ['1', '3', '4', '8'] 3^4 = 81
    ['0', '1', '3', '4', '8'] 3^4 = 81 (+leading zero)
    ['2', '3', '6', '7', '9'] 3^6 = 729
    ['0', '2', '3', '6', '7', '9'] 3^6 = 729 (+leading zero)
    ['2', '3', '8'] 2^3 = 8
    ['0', '2', '3', '8'] 2^3 = 8 (+leading zero)
    ['2', '3', '9'] 3^2 = 9
    ['0', '2', '3', '9'] 3^2 = 9 (+leading zero)
    ['2', '4', '6', '8'] 8^2 = 64
    ['0', '2', '4', '6', '8'] 8^2 = 64 (+leading zero)
    ['2', '4', '7', '9'] 7^2 = 49
    ['0', '2', '4', '7', '9'] 7^2 = 49 (+leading zero)

Ninguno de ellos coincide con el problema (que también se puede ver por la ausencia de '0', '1', ..., '9').

El código de ejemplo que lo resuelve sigue. También tenga en cuenta que está escrito en Python, no porque necesite enteros de precisión arbitrarios (el código no calcula nada más grande que 98 millones), sino porque descubrimos que la cantidad de pruebas es tan pequeña que deberíamos usar un lenguaje de alto nivel para haga uso de sus contenedores y bibliotecas incorporados (también tenga en cuenta: el código tiene 28 líneas).

    import math

    m = 98765432
    l = []
    for i in xrange(2, 98765432):
        inv = 1.0/i
        r = m**inv
        if (r < 2.0): break
        top = int(math.floor(r))
        assert(top <= m)

        for j in xrange(2, top+1):
            s = str(i) + str(j) + str(j**i)
            l.append((sorted(s), i, j, j**i))
            assert(j**i <= m)

    l.sort()
    for s, i, j, ji in l:
        assert(ji <= m)
        ss = sorted(set(s))
        if s == ss:
            print '%s %d^%d = %d' % (s, i, j, ji)

        # Try with non significant zero somewhere
        s = ['0'] + s
        ss = sorted(set(s))
        if s == ss:
            print '%s %d^%d = %d (+leading zero)' % (s, i, j, ji)
hdante
fuente
1
¿por qué no usas 9.876.543.210 como límite superior?
Tom Roggero
3
Porque se deben usar 2 dígitos para el lado izquierdo de la ecuación.
hdante
2
No es que haga la diferencia, pero el límite superior puede tomarse como 98765410, ya que usted ha establecido que los valores en el LHS son> 1
Paul Childs
24

Aquí hay una solución "no portátil" a la pregunta. Las CPU Intel x86 y x64 tienen el denominado registro EFLAGS , que el procesador completa después de cada operación aritmética de enteros. Omitiré una descripción detallada aquí. Las banderas relevantes son la Bandera de "Desbordamiento" (máscara 0x800) y la Bandera de "Llevar" (máscara 0x1). Para interpretarlos correctamente, uno debe considerar si los operandos son de tipo con signo o sin signo.

Aquí hay una forma práctica de verificar las banderas desde C / C ++. El siguiente código funcionará en Visual Studio 2005 o posterior (tanto de 32 como de 64 bits), así como en GNU C / C ++ de 64 bits.

#include <cstddef>
#if defined( _MSC_VER )
#include <intrin.h>
#endif

inline size_t query_intel_x86_eflags(const size_t query_bit_mask)
{
    #if defined( _MSC_VER )

        return __readeflags() & query_bit_mask;

    #elif defined( __GNUC__ )
        // This code will work only on 64-bit GNU-C machines.
        // Tested and does NOT work with Intel C++ 10.1!
        size_t eflags;
        __asm__ __volatile__(
            "pushfq \n\t"
            "pop %%rax\n\t"
            "movq %%rax, %0\n\t"
            :"=r"(eflags)
            :
            :"%rax"
            );
        return eflags & query_bit_mask;

    #else

        #pragma message("No inline assembly will work with this compiler!")
            return 0;
    #endif
}

int main(int argc, char **argv)
{
    int x = 1000000000;
    int y = 20000;
    int z = x * y;
    int f = query_intel_x86_eflags(0x801);
    printf("%X\n", f);
}

Si los operandos se multiplicaron sin desbordamiento, obtendría un valor de retorno de 0 query_intel_eflags(0x801), es decir, no se establecen ni los indicadores de acarreo ni de desbordamiento. En el código de ejemplo proporcionado de main (), se produce un desbordamiento y los dos indicadores se establecen en 1. Esta comprobación no implica ningún cálculo adicional, por lo que debería ser bastante rápido.

Angel Sinigersky
fuente
22

Si tiene un tipo de datos que es más grande que el que desea probar (digamos que agrega 32 bits y tiene un tipo de 64 bits), esto detectará si ocurrió un desbordamiento. Mi ejemplo es para un complemento de 8 bits. Pero se puede ampliar.

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);

Se basa en los conceptos explicados en esta página: http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/Comb/overflow.html

Para un ejemplo de 32 bits, se 0xFFconvierte 0xFFFFFFFFy se 0x80convierte 0x80000000y finalmente se uint16_tconvierte en a uint64_t.

NOTA : esto detecta desbordamientos de suma / resta enteros, y me di cuenta de que su pregunta implica la multiplicación. En cuyo caso, la división es probablemente el mejor enfoque. Esta es comúnmente una forma en que las callocimplementaciones se aseguran de que los parámetros no se desborden, ya que se multiplican para obtener el tamaño final.

Evan Teran
fuente
El enlace está roto: HTTP 403: Prohibido
Peter Mortensen
18

La forma más simple es convertir su unsigned longs en unsigned long longs, hacer su multiplicación y comparar el resultado con 0x100000000LL.

Probablemente encontrará que esto es más eficiente que hacer la división como lo hizo en su ejemplo.

Ah, y funcionará tanto en C como en C ++ (ya que ha etiquetado la pregunta con ambos).


Acabo de leer el manual de glibc . Hay una mención de una trampa de desbordamiento de enteros ( FPE_INTOVF_TRAP) como parte de SIGFPE. Eso sería ideal, aparte de las partes desagradables en el manual:

FPE_INTOVF_TRAP Desbordamiento de enteros (imposible en un programa en C a menos que habilite la captura de desbordamiento de una manera específica de hardware).

Un poco de vergüenza realmente.

Andrew Edgecombe
fuente
44
Je ... lo que no dije fue que estoy haciendo esta pregunta en preparación para escribir un programa para resolver un problema con números más grandes, en el que ya estoy usando int largo largo. Como long long int no está (supuestamente) en el estándar C ++, me quedé con la versión de 32 bits para evitar confusiones.
Chris Johnson
Aconsejaría usar ULONG_MAXcuál es más fácil de escribir y más portátil que la codificación rígida 0x100000000.
jw013
24
Esto no funciona cuando longy long longson del mismo tamaño (por ejemplo, en muchos compiladores de 64 bits).
Interjay
Confiar en las señales para informarle sobre desbordamientos sería muy lento de todos modos.
SamB
@SamB Solo si se esperaba que los desbordamientos fueran frecuentes.
user253751
18

Aquí hay una manera realmente rápida de detectar el desbordamiento de al menos las adiciones, lo que podría dar lugar a la multiplicación, división y potencia de.

La idea es que exactamente porque el procesador simplemente permitirá que el valor vuelva a cero y que C / C ++ se abstraiga de cualquier procesador específico, puede:

uint32_t x, y;
uint32_t value = x + y;
bool overflow = value < (x | y);

Esto garantiza que si un operando es cero y el otro no, entonces el desbordamiento no se detectará falsamente y es significativamente más rápido que muchas operaciones NOT / XOR / AND / test como se sugirió anteriormente.

Como se señaló, este enfoque, aunque es mejor que otras formas más elaboradas, sigue siendo optimizable. La siguiente es una revisión del código original que contiene la optimización:

uint32_t x, y;
uint32_t value = x + y;
const bool overflow = value < x; // Alternatively "value < y" should also work

Una forma más eficiente y económica de detectar el desbordamiento de multiplicación es:

uint32_t x, y;
const bool overflow = (x >> 16U) * (y >> 16U);
uint32_t value = overflow ? UINT32_MAX : x * y;

Esto da como resultado UINT32_MAX en desbordamiento o el resultado de la multiplicación. Es un comportamiento estrictamente indefinido permitir que la multiplicación proceda para enteros con signo en este caso.

DX-MON
fuente
No estoy de acuerdo debido a la teoría de la computación ... considere lo siguiente: y> x, el valor se desborda, y solo es más grande que x debido al bit de signo que se está configurando (1 + 255, por ejemplo, para caracteres sin signo) y el resultado sería x en desbordamiento = falso - de ahí el uso de lógica o para evitar este comportamiento roto ..
DX-MON
La prueba funciona para los números que proporciona (x: = 1, y: = 255, size = uint8_t): el valor será 0 (1 + 255) y 0 <1 es verdadero. Funciona de hecho para cada par de números.
Gunther Piez
Hmm, haces un buen punto. Sin embargo, me mantengo del lado de la seguridad usando el truco o, ya que cualquier buen compilador lo optimizaría si fuera un proveedor. De hecho, es correcto para todas las entradas, incluidos los números que no se desbordan, como "0 + 4", donde el resultado no se desbordaría.
DX-MON
44
Si hay un desbordamiento, entonces x+y>=256y value=x+y-256. Como y<256siempre es cierto, (y-256) es negativo y value < xsiempre es cierto. La prueba para el caso no desbordante es bastante similar.
Gunther Piez
2
@ DX-MON: Su primer método es necesario si también tiene un bit de acarreo de un complemento anterior. uint32_t x[N], y[N], z[N], carry=0; for (int i = 0; i < N; i++) { z[i] = x[i] + y[i] + carry; carry = z[i] < (x[i] | y[i]); }Si no conoce orlos valores, no podrá distinguir entre un operando y el bit de acarreo es cero y un operando es 0xffffffffy el bit de acarreo es uno.
Matt
14

No puede acceder al indicador de desbordamiento desde C / C ++.

Algunos compiladores le permiten insertar instrucciones de captura en el código. En GCC la opción es -ftrapv.

Lo único que puede hacer independientemente del compilador portátil y es verificar los desbordamientos por su cuenta. Tal como lo hiciste en tu ejemplo.

Sin embargo, -ftrapvparece no hacer nada en x86 usando el último GCC. Supongo que es un remanente de una versión anterior o específica de alguna otra arquitectura. Esperaba que el compilador insertara un código de operación INTO después de cada adición. Lamentablemente no hace esto.

Nils Pipenbrinck
fuente
Tal vez varía: -ftrapv parece funcionar bien con GCC 4.3.4 en un cuadro de Cygwin. Hay un ejemplo en stackoverflow.com/questions/5005379/…
Nate Kohl
3
Los dos tienen razón. -ftrapv hace el trabajo pero solo para enteros firmados
ZAB
14

Para enteros sin signo, solo verifique que el resultado sea más pequeño que uno de los argumentos:

unsigned int r, a, b;
r = a + b;
if (r < a)
{
    // Overflow
}

Para enteros con signo, puede verificar los signos de los argumentos y del resultado.

Los enteros de diferentes signos no pueden desbordarse, y los enteros del mismo signo se desbordan solo si el resultado es de un signo diferente:

signed int r, a, b, s;
r = a + b;
s = a>=0;
if (s == (b>=0) && s != (r>=0))
{
    // Overflow
}
Peter Mortensen
fuente
Bueno, el primer método también funcionaría para enteros con signo, ¿no? char result = (char)127 + (char)3;sería -126; más pequeño que ambos operandos.
primfaktor
1
Ah, ya veo, el problema es el hecho de que no está definido para los tipos firmados.
primfaktor
27
-1 desbordamiento de números con signo da como resultado un comportamiento indefinido (por lo tanto, la prueba es demasiado tarde para ser realmente útil).
Voo
1
@primfaktor no funciona para int firmado: char ((- 127) + (-17)) = 112. Para int firmado debe verificar el bit de signo de los argumentos y el resultado
phuclv
3
Como ya se dijo, la solución para el entero con signo no funciona debido al comportamiento indefinido de a + b en caso de desbordamiento. La comprobación de desbordamiento con entero con signo debe realizarse antes de la operación.
Marwan Burelle el
11

Necesitaba responder esta misma pregunta para los números de coma flotante, donde el enmascaramiento y el desplazamiento de bits no parecen prometedores. El enfoque en el que me decidí funciona para números con signo y sin signo, enteros y de coma flotante. Funciona incluso si no hay un tipo de datos más grande para promover los cálculos intermedios. No es el más eficiente para todos estos tipos, pero como funciona para todos ellos, vale la pena usarlo.

Prueba de desbordamiento firmada, suma y resta:

  1. Obtenga las constantes que representan los valores más grandes y más pequeños posibles para el tipo, MAXVALUE y MINVALUE.

  2. Calcule y compare los signos de los operandos.

    a. Si cualquiera de los valores es cero, entonces ni la suma ni la resta pueden desbordarse. Omita las pruebas restantes.

    si. Si los signos son opuestos, entonces la suma no puede desbordarse. Omita las pruebas restantes.

    C. Si los signos son iguales, entonces la resta no puede desbordarse. Omita las pruebas restantes.

  3. Prueba de desbordamiento positivo de MAXVALUE.

    a. Si ambos signos son positivos y MAXVALUE - A <B, entonces la adición se desbordará.

    si. Si el signo de B es negativo y MAXVALUE - A <-B, entonces la resta se desbordará.

  4. Prueba de desbordamiento negativo de MINVALUE.

    a. Si ambos signos son negativos y MINVALUE - A> B, la adición se desbordará.

    si. Si el signo de A es negativo y MINVALUE - A> B, entonces la resta se desbordará.

  5. De lo contrario, no hay desbordamiento.

Prueba de desbordamiento firmada, multiplicación y división:

  1. Obtenga las constantes que representan los valores más grandes y más pequeños posibles para el tipo, MAXVALUE y MINVALUE.

  2. Calcule y compare las magnitudes (valores absolutos) de los operandos con uno. (A continuación, suponga que A y B son estas magnitudes, no los originales firmados).

    a. Si cualquiera de los valores es cero, la multiplicación no puede desbordarse y la división producirá cero o un infinito.

    si. Si cualquiera de los valores es uno, la multiplicación y la división no pueden desbordarse.

    C. Si la magnitud de un operando está por debajo de uno y el otro es mayor que uno, la multiplicación no puede desbordarse.

    re. Si las magnitudes son menos de uno, la división no puede desbordarse.

  3. Prueba de desbordamiento positivo de MAXVALUE.

    a. Si ambos operandos son mayores que uno y MAXVALUE / A <B, entonces la multiplicación se desbordará.

    si. Si B es menor que uno y MAXVALUE * B <A, entonces la división se desbordará.

  4. De lo contrario, no hay desbordamiento.

Nota: El desbordamiento mínimo de MINVALUE es manejado por 3, porque tomamos valores absolutos. Sin embargo, si ABS (MINVALUE)> MAXVALUE, tendremos algunos falsos positivos raros.

Las pruebas de subflujo son similares, pero involucran EPSILON (el número positivo más pequeño mayor que cero).

Paul Chernoch
fuente
1
Al menos en los sistemas POSIX, la señal SIGFPE se puede habilitar para puntos flotantes bajo / desbordamientos.
Chris Johnson
Si bien la conversión a coma flotante y viceversa es (según mis pruebas en una máquina de 32 bits) mucho más lenta que las otras soluciones.
JanKanis
Un revisor detectó un caso que faltaba para la resta, parte 2. Estoy de acuerdo en que 0 - MINVALUE se desbordaría. Por lo tanto, se deben agregar pruebas para este caso.
Paul Chernoch
<pedantic> Los enteros no se desbordan (= se acercan demasiado a cero para ser representados con precisión). 1.0e-200 / 1.0e200sería un ejemplo de un flujo inferior real, suponiendo que IEEE se duplica. El término correcto aquí, en cambio, es desbordamiento negativo. </pedantic>
Arne Vogel
Para ser precisos, la razón por la que no se considera que los enteros se desborden es debido a un comportamiento de truncamiento definido, por ejemplo, 1/INT_MAXpodría considerarse un subflujo, pero el lenguaje simplemente ordena el truncamiento a cero.
Arne Vogel
8

El CERT ha desarrollado un nuevo enfoque para detectar e informar el desbordamiento de enteros firmados, el ajuste de enteros sin firmar y el truncamiento de enteros utilizando el modelo de enteros de rango infinito "AIR". El CERT ha publicado un informe técnico que describe el modelo y produjo un prototipo funcional basado en GCC 4.4.0 y GCC 4.5.0.

El modelo entero AIR produce un valor equivalente a uno que se habría obtenido usando enteros de rango infinito o da como resultado una violación de restricción de tiempo de ejecución. A diferencia de los modelos enteros anteriores, los enteros AIR no requieren trampas precisas y, en consecuencia, no rompen ni inhiben la mayoría de las optimizaciones existentes.

Robert C. Seacord
fuente
No vi nada útil en el enlace, pero eso suena como un modelo que he defendido durante mucho tiempo. Admite la gran mayoría de las optimizaciones útiles, a la vez que admite garantías semánticas útiles que la mayoría de las implementaciones pueden proporcionar esencialmente sin cargo. Si el código sabe que las entradas a una función serán válidas en todos los casos en los que la salida importa , pero no sabe de antemano si la salida importará, puede permitir que se produzcan desbordamientos en los casos en que no afecten nada. más fácil y más eficiente que tener que prevenirlos a toda costa.
supercat
8

Otra herramienta interesante es IOC: un comprobador de desbordamiento de enteros para C / C ++ .

Este es un compilador de Clang parcheado , que agrega controles al código en tiempo de compilación.

Obtiene una salida como esta:

CLANG ARITHMETIC UNDEFINED at <add.c, (9:11)> :
Op: +, Reason : Signed Addition Overflow,
BINARY OPERATION: left (int32): 2147483647 right (int32): 1
Willem Hengeveld
fuente
1
Este parche ahora se fusionó con la base de códigos clang entre otros desinfectantes, vea mi respuesta.
ZAB
7

Otra variante de una solución, usando lenguaje ensamblador, es un procedimiento externo. Este ejemplo para la multiplicación de enteros sin signo usando g ++ y fasm en Linux x64.

Este procedimiento multiplica dos argumentos enteros sin signo (32 bits) (de acuerdo con la especificación para amd64 (sección 3.2.3 Paso de parámetros ).

Si la clase es INTEGER, se utiliza el siguiente registro disponible de la secuencia% rdi,% rsi,% rdx,% rcx,% r8 y% r9

(edi y esi se registran en mi código)) y devuelve el resultado o 0 si se ha producido un desbordamiento.

format ELF64

section '.text' executable

public u_mul

u_mul:
  MOV eax, edi
  mul esi
  jnc u_mul_ret
  xor eax, eax
u_mul_ret:
ret

Prueba:

extern "C" unsigned int u_mul(const unsigned int a, const unsigned int b);

int main() {
    printf("%u\n", u_mul(4000000000,2)); // 0
    printf("%u\n", u_mul(UINT_MAX/2,2)); // OK
    return 0;
}

Enlace el programa con el archivo de objeto asm. En mi caso, en Qt Creator , agréguelo a LIBSun archivo .pro.

Bartolo-otrit
fuente
5

Calcule los resultados con dobles. Tienen 15 dígitos significativos. Su requisito tiene un límite superior duro en c de 10 8  : puede tener como máximo 8 dígitos. Por lo tanto, el resultado será preciso si está dentro del rango, y de lo contrario no se desbordará.

MSalters
fuente
5

Pruebe esta macro para probar el bit de desbordamiento de las máquinas de 32 bits (adaptó la solución de Angel Sinigersky)

#define overflowflag(isOverflow){   \
size_t eflags;                      \
asm ("pushfl ;"                     \
     "pop %%eax"                    \
    : "=a" (eflags));               \
isOverflow = (eflags >> 11) & 1;}

Lo definí como una macro porque de lo contrario el bit de desbordamiento se habría sobrescrito.

Posterior es una pequeña aplicación con el segmento de código anterior:

#include <cstddef>
#include <stdio.h>
#include <iostream>
#include <conio.h>
#if defined( _MSC_VER )
#include <intrin.h>
#include <oskit/x86>
#endif

using namespace std;

#define detectOverflow(isOverflow){     \
size_t eflags;                      \
asm ("pushfl ;"                     \
    "pop %%eax"                     \
    : "=a" (eflags));               \
isOverflow = (eflags >> 11) & 1;}

int main(int argc, char **argv) {

    bool endTest = false;
    bool isOverflow;

    do {
        cout << "Enter two intergers" << endl;
        int x = 0;
        int y = 0;
        cin.clear();
        cin >> x >> y;
        int z = x * y;
        detectOverflow(isOverflow)
        printf("\nThe result is: %d", z);
        if (!isOverflow) {
            std::cout << ": no overflow occured\n" << std::endl;
        } else {
            std::cout << ": overflow occured\n" << std::endl;
        }

        z = x * x * y;
        detectOverflow(isOverflow)
        printf("\nThe result is: %d", z);
        if (!isOverflow) {
            std::cout << ": no overflow ocurred\n" << std::endl;
        } else {
            std::cout << ": overflow occured\n" << std::endl;
        }

        cout << "Do you want to stop? (Enter \"y\" or \"Y)" << endl;

        char c = 0;

        do {
            c = getchar();
        } while ((c == '\n') && (c != EOF));

        if (c == 'y' || c == 'Y') {
            endTest = true;
        }

        do {
            c = getchar();
        } while ((c != '\n') && (c != EOF));

    } while (!endTest);
}
Markus Demarmels
fuente
44
No todas las máquinas de 32 bits son compatibles con Intel x86, y no todos los compiladores admiten la sintaxis de ensamblaje gnu (me parece curioso que publique el código que prueba, _MSC_VERaunque las compilaciones de MS rechazarán el código).
Ben Voigt
2

Catching Integer Overflows en C señala una solución más general que la discutida por CERT (es más general en términos de tipos manejados), incluso si requiere algunas extensiones de GCC (no sé cuán ampliamente son compatibles).

Blaisorblade
fuente
2

No puede acceder al indicador de desbordamiento desde C / C ++.

No estoy de acuerdo con esto Puede escribir un lenguaje ensamblador en línea y usar una joinstrucción (salto de desbordamiento) suponiendo que está en x86 para atrapar el desbordamiento. Por supuesto, su código ya no sería portátil para otras arquitecturas.

Mira info asy info gcc.

Tarski
fuente
8
El ensamblador en línea no es una característica de C / C ++ y es independiente de la plataforma. En x86 puede usar la instrucción into en lugar de ramas por cierto.
Nils Pipenbrinck
0

Para ampliar la respuesta de Head Geek, hay una forma más rápida de hacer el addition_is_safe;

bool addition_is_safe(unsigned int a, unsigned int b)
{
    unsigned int L_Mask = std::numeric_limits<unsigned int>::max();
    L_Mask >>= 1;
    L_Mask = ~L_Mask;

    a &= L_Mask;
    b &= L_Mask;

    return ( a == 0 || b == 0 );
}

Esto utiliza una arquitectura de máquina segura, ya que los enteros sin signo de 64 bits y 32 bits seguirán funcionando bien. Básicamente, creo una máscara que enmascarará todo menos el bit más significativo. Luego, enmascaro ambos enteros, y si alguno de ellos no tiene ese bit establecido, entonces la suma es segura.

Esto sería aún más rápido si preinicializa la máscara en algún constructor, ya que nunca cambia.

Steztric
fuente
55
Esto no es correcto. Llevar puede traer pedazos de las posiciones más bajas que causarán desbordamiento. Considera agregar UINT_MAX + 1. Después del enmascaramiento, ase establecerá el bit alto, pero 1se convertirá en cero y, por lo tanto, la función volverá true, la adición es segura, pero se dirige directamente al desbordamiento.
los cerdos
0

mozilla::CheckedInt<T>proporciona matemática de enteros con desbordamiento para el tipo de entero T(utilizando intrínsecos del compilador en clang y gcc según esté disponible). El código es bajo MPL 2.0 y depende de tres ( IntegerTypeTraits.h, Attributes.hy Compiler.h) otros sólo de encabezado no estándar cabeceras Library Plus Mozilla específica maquinaria afirmación . Probablemente desee reemplazar la maquinaria de aserción si importa el código.

hsivonen
fuente
-1

La respuesta de MSalter es una buena idea.

Si se requiere el cálculo de enteros (para precisión), pero el punto flotante está disponible, puede hacer algo como:

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
    }
}
Frank Szczerba
fuente
Por lo general, diría que repetir el cálculo en coma flotante es una mala idea, pero para este caso específico de exponenciación a ^ c, puede ser más eficiente. Pero la prueba debería ser (c * log(a) < max_log), dondeconst double max_log = log(UINT_MAX)
Toby Speight
-1

El conjunto de instrucciones x86 incluye una instrucción de multiplicación sin signo que almacena el resultado en dos registros. Para usar esa instrucción de C, se puede escribir el siguiente código en un programa de 64 bits (GCC):

unsigned long checked_imul(unsigned long a, unsigned long b) {
  unsigned __int128 res = (unsigned __int128)a * b;
  if ((unsigned long)(res >> 64))
    printf("overflow in integer multiply");
  return (unsigned long)res;
}

Para un programa de 32 bits, uno necesita hacer que el resultado sea de 64 bits y los parámetros de 32 bits.

Una alternativa es utilizar el intrínseco dependiente del compilador para verificar el registro del indicador. La documentación de GCC para el desbordamiento intrínseco se puede encontrar en 6.56 Funciones incorporadas para realizar operaciones aritméticas con verificación de desbordamiento .

Pauli Nieminen
fuente
1
Debe usar el tipo de 128 bits sin signo __uint128para evitar el desbordamiento firmado y el desplazamiento a la derecha de un valor negativo.
chqrlie
¿Qué son los "instintos dependientes del compilador" y los "instintos de desbordamiento" ? ¿Te refieres a " funciones intrínsecas " ? Tiene una referencia? (Responda editando su respuesta , no aquí en los comentarios (según corresponda).)
Peter Mortensen el
-3
#include <stdio.h>
#include <stdlib.h>

#define MAX 100 

int mltovf(int a, int b)
{
    if (a && b) return abs(a) > MAX/abs(b);
    else return 0;
}

main()
{
    int a, b;

    for (a = 0; a <= MAX; a++)
        for (b = 0; b < MAX; b++) {

        if (mltovf(a, b) != (a*b > MAX)) 
            printf("Bad calculation: a: %d b: %d\n", a, b);

    }
}
Scott Franco
fuente
-3

Una forma limpia de hacerlo sería anular todos los operadores (+ y * en particular) y verificar un desbordamiento antes de realizar las operaciones.

Brian R. Bondy
fuente
66
Excepto que no puede anular operadores para tipos incorporados. Tendría que escribir una clase para eso y reescribir el código del cliente para usarlo.
Blaisorblade
-3

Depende de para qué lo uses. Al realizar sumas o multiplicaciones largas sin signo (DWORD), la mejor solución es usar ULARGE_INTEGER.

ULARGE_INTEGER es una estructura de dos DWORD. Se puede acceder al valor completo como "QuadPart", mientras que la DWORD alta se accede como "HighPart" y la DWORD baja se accede como "LowPart".

Por ejemplo:

DWORD
My Addition(DWORD Value_A, DWORD Value_B)
{
    ULARGE_INTEGER a, b;

    b.LowPart = Value_A;  // A 32 bit value(up to 32 bit)
    b.HighPart = 0;
    a.LowPart = Value_B;  // A 32 bit value(up to 32 bit)
    a.HighPart = 0;

    a.QuadPart += b.QuadPart;

    // If  a.HighPart
    // Then a.HighPart contains the overflow (carry)

    return (a.LowPart + a.HighPart)

    // Any overflow is stored in a.HighPart (up to 32 bits)
Spyros Panaoussis
fuente
66
Desafortunadamente, esta es una solución solo para Windows. Otras plataformas no tienen ULARGE_INTEGER.
Mysticial
-3

Para realizar una multiplicación sin signo sin desbordarse de forma portátil, se puede utilizar lo siguiente:

... /* begin multiplication */
unsigned multiplicand, multiplier, product, productHalf;
int zeroesMultiplicand, zeroesMultiplier;
zeroesMultiplicand = number_of_leading_zeroes( multiplicand );
zeroesMultiplier   = number_of_leading_zeroes( multiplier );
if( zeroesMultiplicand + zeroesMultiplier <= 30 ) goto overflow;
productHalf = multiplicand * ( c >> 1 );
if( (int)productHalf < 0 ) goto overflow;
product = productHalf * 2;
if( multiplier & 1 ){
   product += multiplicand;
   if( product < multiplicand ) goto overflow;
}
..../* continue code here where "product" is the correct product */
....
overflow: /* put overflow handling code here */

int number_of_leading_zeroes( unsigned value ){
   int ctZeroes;
   if( value == 0 ) return 32;
   ctZeroes = 1;
   if( ( value >> 16 ) == 0 ){ ctZeroes += 16; value = value << 16; }
   if( ( value >> 24 ) == 0 ){ ctZeroes +=  8; value = value <<  8; }
   if( ( value >> 28 ) == 0 ){ ctZeroes +=  4; value = value <<  4; }
   if( ( value >> 30 ) == 0 ){ ctZeroes +=  2; value = value <<  2; }
   ctZeroes -= x >> 31;
   return ctZeroes;
}
Tyler Durden
fuente
-4

La forma sencilla de probar el desbordamiento es validar comprobando si el valor actual es menor que el valor anterior. Por ejemplo, suponga que tiene un bucle para imprimir las potencias de 2:

long lng;
int n;
for (n = 0; n < 34; ++n)
{
   lng = pow (2, n);
   printf ("%li\n", lng);
}

Agregar desbordamiento comprobando la forma en que describí los resultados en esto:

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;
}

Funciona tanto para valores sin signo como para valores con signo positivo y negativo.

Por supuesto, si quisiera hacer algo similar para disminuir los valores en lugar de aumentar los valores, voltearía el <=signo para hacerlo >=, suponiendo que el comportamiento del desbordamiento sea el mismo que el del desbordamiento. Honestamente, eso es lo más portátil posible sin acceso al indicador de desbordamiento de una CPU (y eso requeriría un código de ensamblaje en línea, lo que hace que su código no sea portátil en todas las implementaciones de todos modos).

Dustin
fuente
99
Si se desborda un valor firmado, el comportamiento de su programa no está definido. No se garantiza su envoltura.
David Stone