bitlvl
ES

Calculadora de desbordamiento y aritmética de enteros

Escribe una expresión, elige un tipo y un lenguaje y mira exactamente qué hace la máquina: qué operandos se promocionan o convierten, dónde deja de caber el resultado, si eso es wraparound, un panic o comportamiento indefinido, y los bits antes y después.

Los resultados y mensajes de esta herramienta se muestran en inglés.

Por qué los enteros sorprenden

Un entero de máquina tiene un número fijo de bits, así que cualquier operación puede producir un valor que no cabe. Lo que pasa entonces no lo decide el hardware (que simplemente da la vuelta), sino el lenguaje:

LenguajeDesbordamiento con signoDesbordamiento sin signoDesplazamiento ≥ anchuraDivisión entre cero
C / C++comportamiento indefinido: el optimizador asume que nunca ocurreda la vuelta módulo 2ⁿ (definido)indefinidoindefinido (SIGFPE en x86)
Rustpanic en debug, vuelta en release; checked_/wrapping_/saturating_/overflowing_ son explícitosigual que con signopanic en debugpanic
Goda la vuelta (definido)da la vuelta0 / −1, sin panicpanic
Javada la vuelta (definido); Math.addExact lanzano hay tipos sin signo (char es unsigned de 16 bits)contador enmascarado a 5/6 bitsArithmeticException
JavaScriptlos números son doubles: exactos hasta 2⁵³, luego se redondean; los operadores de bits trabajan sobre int32contador enmascarado a 5 bitsInfinity / NaN

Las reglas de C que muerden

  • Promoción de enteros. Todo lo más pequeño que int se convierte en int antes de la aritmética, así que uint8_t a = 200, b = 100; a + b es 300, y solo la asignación de vuelta a un uint8_t lo trunca a 44. ~x sobre un uint8_t da un int negativo.
  • Conversiones aritméticas usuales. Cuando un signed se encuentra con un unsigned de la misma anchura, el lado signed se vuelve unsigned: -1 > 0u es verdadero, if (len - 1 < 0) nunca es verdadero si len es un size_t.
  • El desbordamiento con signo es UB. if (x + 1 < x) como comprobación de desbordamiento lo eliminan GCC y Clang con optimización, porque pueden asumir que x + 1 nunca desborda. Comprueba antes de la operación (x > INT_MAX - y) o usa __builtin_add_overflow.
  • Desplazamientos. Desplazar la anchura o más, o desplazar a la izquierda un valor negativo, es indefinido; x86 enmascara el contador, ARM da cero, el compilador hace lo que diga su modelo.
  • División. Trunca hacia cero, así que -7 / 2 == -3 y -7 % 2 == -1; INT_MIN / -1 desborda y de hecho provoca un trap en x86.

Leer la salida

El resultado se muestra en el tipo en el que acaba, con su patrón de bits y los mismos bits en la lectura de signo opuesta, es decir, lo que ves cuando un valor cruza un límite (int)/(unsigned) o se guarda a través de otro tipo y se vuelve a leer. La lista de pasos es el orden de evaluación con el tipo en cada paso; las notas son los puntos donde el lenguaje hizo algo distinto de la aritmética simple.

Preguntas

¿Por qué el resultado en C difiere de lo que imprimió mi programa?

Para el comportamiento indefinido no hay una única respuesta correcta: esta página muestra el valor de wraparound que produce el código sin optimizar y marca el paso como UB. Con -O2, el compilador puede haber asumido que el desbordamiento no puede ocurrir y generado otro código. Compila con -fsanitize=undefined para ver el punto exacto.

¿Qué hace el selector de tipo?

Fija el tipo de los literales sin sufijo ni cast, que en C es int por defecto. Elige u8 y escribe 200 + 100 para ver a Rust hacer panic, o a C promocionar a int y tener éxito.

¿Cómo escribo un cast?

Estilo C: (u8) expr, (uint32_t) expr, (int) expr. Estilo Rust: expr as u8. Ambos funcionan en cualquier modo de lenguaje. INT_MAX, INT_MIN, UINT_MAX, LONG_MAX, SIZE_MAX están disponibles como nombres.

¿Maneja 128 bits?

Sí: i128 y u128 se evalúan con exactitud (toda la aritmética se hace con precisión arbitraria y luego se ajusta al tipo).

¿Y la coma flotante?

Aquí no; para eso está el visualizador de floats IEEE 754 de este sitio. Esta herramienta trata solo lo que pasa con los números enteros en tipos de anchura fija.