c++ - que - ¿Por qué es el comportamiento definido de desbordamiento de enteros sin signo pero el desbordamiento de enteros con signo no?
que es el desbordamiento en informatica (5)
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
e Y
, existirá algún otro valor Z
tal que X+Z
, si se lanza correctamente, será igual a Y
e YZ
, si se lanza correctamente, será igual a X
). Si los valores sin signo fueran simplemente tipos de ubicación de almacenamiento y no de expresión intermedia (por ejemplo, si no hubiera un equivalente sin signo del tipo de entero más grande y las operaciones aritméticas sobre tipos sin firmar se comportaron como si primero se convirtieran a tipos firmados más grandes, entonces no sería tanta la necesidad de un comportamiento de envoltura definido, pero es difícil hacer cálculos en un tipo que no tiene, por ejemplo, un aditivo inverso.
Esto ayuda en situaciones donde el comportamiento de envolvente es realmente útil, por ejemplo, con números de secuencia TCP o ciertos algoritmos, como el cálculo de hash. También puede ser útil 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 con anticipación si se desbordarían, especialmente si los cálculos involucran el tipo de entero más grande disponible.
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 involucre operandos sin firmar nunca puede sobrevolverse, porque un resultado que no puede ser representado por el tipo de entero sin signo resultante es un módulo reducido que es uno mayor que el valor más grande que puede ser representado por el tipo resultante.
Sin embargo, ambos estándares indican que el desbordamiento de entero con signo es un comportamiento indefinido. Nuevamente, del estándar C99 ( §3.4.3/1
)
Un ejemplo de comportamiento indefinido es el comportamiento en el sobreflujo de enteros
¿Existe una razón técnica histórica o (¡incluso mejor!) Para esta discrepancia?
Aparte de la buena respuesta de Pascal (que estoy seguro es la motivación principal), también es posible que algunos procesadores provoquen una excepción en el desbordamiento de entero 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 posible desbordamiento y calcule de manera diferente en ese caso).
También vale la pena señalar que el "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 es 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 resulta, y que el compilador PUEDE hacer algo diferente de lo que espera (y que esto puede depender de la versión del compilador, la configuración de optimización, etc.) .
En primer lugar, tenga en cuenta que C11 3.4.3, como todos los ejemplos y notas al pie, no es 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 este:
C11 6.5 / 5
Si se produce una condición excepcional durante la evaluación de una expresión (es decir, si el resultado no está definido matemáticamente o no está en el rango de valores representables para su tipo), el comportamiento no está definido.
Aquí puede encontrar una aclaración sobre el comportamiento de los tipos enteros sin signo específicamente:
C11 6.2.5 / 9
El rango de valores no negativos de un tipo entero con signo es un subrango del tipo entero sin signo correspondiente, y la representación del mismo valor en cada tipo es la misma. Un cómputo que involucre operandos sin firmar nunca puede desbordarse, porque un resultado que no puede ser representado por el tipo de entero sin signo resultante se reduce en módulo al número que es uno mayor que el valor más grande que puede ser representado por el tipo resultante.
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 a un tipo firmado y el valor anterior ya no se puede representar. El comportamiento es simplemente definido por la implementación, aunque se puede generar una señal.
C11 6.3.1.3
6.3.1.3 enteros con signo y sin signo
Cuando un valor con tipo entero se convierte a otro tipo entero distinto de _Bool, si el valor puede representarse con el tipo nuevo, no se modifica.
De lo contrario, si el nuevo tipo no está firmado, el valor se convierte al agregar o restar repetidamente uno más que el valor máximo que se puede representar en el nuevo tipo hasta que el valor esté en el rango del nuevo tipo.
De lo contrario, el nuevo tipo se firma y el valor no se puede representar en él; el resultado es definido por la implementación o se genera una señal definida por la implementación.
La razón histórica es que la mayoría de las implementaciones de C (compiladores) simplemente usaron cualquier comportamiento de desbordamiento fue más fácil de implementar con la representación entera que utilizó. Las implementaciones C usualmente usaban la misma representación utilizada por la CPU, por lo que el comportamiento de desbordamiento se seguía de la representación entera utilizada por la CPU.
En la práctica, solo las representaciones de los valores firmados pueden diferir según la implementación: el complemento de uno, el complemento de dos, la magnitud de signo. Para un tipo sin signo no hay ninguna 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 :
Los valores almacenados en campos de bit sin signo y objetos de tipo char sin signo se representarán utilizando una notación binaria pura.
C99 6.2.6.2:2 :
Si el bit de signo es uno, el valor se modificará de una de las siguientes maneras:
- el valor correspondiente con el bit de signo 0 es negado ( signo y magnitud );
- el bit de signo tiene el valor - (2 N ) ( complemento de dos );
- el bit de signo tiene el valor - (2 N - 1) ( complemento de uno ).
Hoy en día, todos los procesadores utilizan dos representaciones de complemento, pero el desbordamiento aritmético firmado permanece indefinido y los fabricantes de compiladores quieren que permanezca indefinido porque usan esta indefinición para ayudar con la optimización. Ver, por ejemplo, esta publicación del blog de Ian Lance Taylor o esta complaint de Agner Fog, y las respuestas a su informe de errores.
Quizás otra razón por la que se define la aritmética sin signo es porque los números sin signo forman 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 con dígitos binarios en lugar de dígitos decimales. Se entiende bien la realización de las operaciones estándar en un sistema de módulo.
La cita de OP se refiere a este hecho, pero también resalta el hecho de que solo hay una forma lógica e inequívoca de representar números enteros sin signo en binario. Por el contrario, los números con signo suelen representarse con el complemento de dos, pero son posibles otras opciones, tal como se describe en el estándar (sección 6.2.6.2).
La representación de dos complementos permite que ciertas operaciones tengan más sentido en formato binario. Por ejemplo, aumentar los números negativos es el mismo que para los números positivos (esperar en condiciones de desbordamiento). Algunas operaciones en el nivel de la máquina pueden ser las mismas para los números con y sin firma. 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.