El desbordamiento de enteros sin signo está bien definido por los estándares C y C ++. Por ejemplo, el estándar C99 ( §6.2.5/9
) establece
Un cómputo que involucra operandos sin signo nunca puede desbordarse, porque un resultado que no puede ser representado por el tipo entero sin signo resultante se reduce módulo el número que es uno mayor que el valor más grande que puede ser representado por el tipo resultante.
Sin embargo, ambos estándares establecen que el desbordamiento de enteros con signo es un comportamiento indefinido. De nuevo, desde el estándar C99 ( §3.4.3/1
)
Un ejemplo de comportamiento indefinido es el comportamiento en el desbordamiento de enteros
¿Existe una razón histórica o (¡incluso mejor!) Técnica para esta discrepancia?
c++
c
undefined-behavior
integer-overflow
Anthony Vallée-Dubois
fuente
fuente
if (a + b < a)
). El desbordamiento en la multiplicación es difícil para los tipos con signo y sin signo.MAX_INT+1 == -0
, mientras que en un complemento de dos seríaINT_MIN
Respuestas:
La razón histórica es que la mayoría de las implementaciones de C (compiladores) simplemente usaron cualquier comportamiento de desbordamiento que fuera más fácil de implementar con la representación entera que usaban. Las implementaciones de C generalmente usaban la misma representación utilizada por la CPU, por lo que el comportamiento de desbordamiento siguió a la representación de enteros utilizada por la CPU.
En la práctica, solo las representaciones de los valores con signo pueden diferir según la implementación: el complemento de uno, el complemento de dos, la magnitud del signo. Para un tipo sin signo, no hay razón para que el estándar permita la variación porque solo hay una representación binaria obvia (el estándar solo permite la representación binaria).
Citas relevantes:
C99 6.2.6.1:3 :
C99 6.2.6.2:2 :
Hoy en día, todos los procesadores usan la representación del complemento de dos, pero el desbordamiento aritmético con signo permanece indefinido y los fabricantes de compiladores quieren que permanezca indefinido porque usan esta indefinición para ayudar con la optimización. Vea, por ejemplo, esta publicación de blog de Ian Lance Taylor o esta queja de Agner Fog, y las respuestas a su informe de error.
fuente
Aparte de la buena respuesta de Pascal (que estoy seguro es la motivación principal), también es posible que algunos procesadores causen una excepción en el desbordamiento de enteros con signo, lo que, por supuesto, causaría problemas si el compilador tuviera que "organizar otro comportamiento" ( por ejemplo, use instrucciones adicionales para verificar el desbordamiento potencial y calcular de manera diferente en ese caso).
También vale la pena señalar que "comportamiento indefinido" no significa "no funciona". Significa que la implementación puede hacer lo que quiera en esa situación. Esto incluye hacer "lo correcto", así como "llamar a la policía" o "estrellarse". La mayoría de los compiladores, cuando sea posible, elegirán "hacer lo correcto", suponiendo que sea relativamente fácil de definir (en este caso, lo es). Sin embargo, si tiene desbordamientos en los cálculos, es importante comprender lo que realmente da como resultado, y que el compilador PUEDE hacer algo diferente a lo que espera (y que esto puede depender mucho de la versión del compilador, la configuración de optimización, etc.) .
fuente
int f(int x) { return x+1>x; }
con optimización. GCC e ICC hacen, con las opciones predeterminadas, optimizar lo anterior parareturn 1;
.int
desbordamiento dependiendo de los niveles de optimización, consulte ideone.com/cki8nM. Creo que esto demuestra que su respuesta da malos consejos.En primer lugar, tenga en cuenta que C11 3.4.3, como todos los ejemplos y notas al pie, no es un texto normativo y, por lo tanto, no es relevante para citar.
El texto relevante que establece que el desbordamiento de enteros y flotantes es un comportamiento indefinido es el siguiente:
C11 6.5 / 5
Aquí se puede encontrar una aclaración sobre el comportamiento de los tipos enteros sin signo:
C11 6.2.5 / 9
Esto hace que los tipos enteros sin signo sean un caso especial.
También tenga en cuenta que hay una excepción si cualquier tipo se convierte en un tipo con signo y el valor anterior ya no se puede representar. El comportamiento se define simplemente por implementación, aunque se puede generar una señal.
C11 6.3.1.3
fuente
Además de los otros problemas mencionados, tener un ajuste matemático sin signo hace que los tipos enteros sin signo se comporten como grupos algebraicos abstractos (lo que significa que, entre otras cosas, para cualquier par de valores
X
yY
, existirá algún otro valorZ
queX+Z
, si se emite correctamente , igualY
yY-Z
voluntad, si se lanza correctamente, igualX
) Si los valores sin signo eran simplemente tipos de ubicación de almacenamiento y no tipos de expresión intermedia (por ejemplo, si no hubiera un equivalente sin signo del tipo entero más grande, y las operaciones aritméticas en tipos sin signo se comportaban como si primero se convirtieran en tipos con signo más grandes, entonces no no sería tan necesario un comportamiento de ajuste definido, pero es difícil hacer cálculos en un tipo que no tiene, por ejemplo, un inverso aditivo.Esto ayuda en situaciones donde el comportamiento envolvente es realmente útil, por ejemplo, con números de secuencia TCP o ciertos algoritmos, como el cálculo de hash. También puede ayudar en situaciones donde es necesario detectar el desbordamiento, ya que realizar cálculos y verificar si se desbordaron a menudo es más fácil que verificar de antemano si se desbordarían, especialmente si los cálculos involucran el tipo entero más grande disponible.
fuente
a+b-c
se calcula dentro de un ciclo, perob
yc
es constante dentro de ese ciclo, puede ser útil mover el cálculo(b-c)
fuera del ciclo, pero hacerlo requeriría, entre otras cosas,(b-c)
un valor que, cuando se agrega aa
, rendiráa+b-c
, lo que a su vez requiere quec
tenga un inverso aditivo.(a+b)-c
iguala+(b-c)
o no que el valor aritmético deb-c
sea representable dentro del tipo, la sustitución será válida independientemente del posible rango de valores para(b-c)
.Quizás otra razón por la cual se define la aritmética sin signo es porque los números sin signo forman números enteros módulo 2 ^ n, donde n es el ancho del número sin signo. Los números sin signo son simplemente números enteros representados usando dígitos binarios en lugar de dígitos decimales. Realización de las operaciones estándar en un sistema de módulo se entiende bien.
La cita del OP se refiere a este hecho, pero también destaca el hecho de que solo hay una forma lógica, inequívoca, de representar enteros sin signo en binario. Por el contrario, los números con signo se representan con mayor frecuencia utilizando el complemento de dos, pero son posibles otras opciones como se describe en el estándar (sección 6.2.6.2).
La representación del complemento a dos permite que ciertas operaciones tengan más sentido en formato binario. Por ejemplo, incrementar números negativos es lo mismo que para números positivos (espere bajo condiciones de desbordamiento). Algunas operaciones a nivel de máquina pueden ser las mismas para números con y sin signo. Sin embargo, al interpretar el resultado de esas operaciones, algunos casos no tienen sentido: desbordamiento positivo y negativo. Además, los resultados de desbordamiento difieren según la representación firmada subyacente.
fuente