Regla 0x5009h: Prohibición de división entera no intencional asignada a flotantes
Compilacion, preprocesador y seguridad (0x50XX)
0x5009h: Prohibición de división entera no intencional asignada a flotantes¶
Enunciado normativo¶
NO DEBE asignarse a una variable flotante el resultado de una división cuyos dos operandos son enteros, salvo que la truncación sea intencional. DEBE promoverse explícitamente un operando con literal flotante o cast.
¿Por qué existe esta regla?¶
El problema¶
El operador / decide su semántica según los tipos de los operandos. Si ambos
son enteros, hace división entera y descarta la parte fraccionaria antes
de que ocurra cualquier conversión a double. La asignación al flotante es
posterior e inútil: el daño ya está hecho. Para que / sea real, al menos un
operando debe ser de punto flotante en el momento de evaluar la expresión.
Es un error silencioso: no hay advertencia del compilador, el programa compila y
devuelve 0.0 en vez de 0.5. El estudiante lo descubre recién al imprimir el
resultado, y suele culpar al formato de salida.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Resultado incorrecto | double t = 1 / 2; guarda 0.0, no 0.5. |
| Bug silencioso | El compilador no advierte; el error viaja hasta la salida. |
| Lógica | Promedios, tasas y porcentajes quedan truncados a cero. |
| División por cero | resto / n con n entero puede abortar donde se esperaba un valor real. |
Fundamento en el estándar y en la cátedra¶
ISO/IEC 9899:2011 §6.5.5 define / para operandos aritméticos y §6.3.1.8 las
conversiones aritméticas usuales: si ambos operandos son enteros, la división es
entera y el resultado tiene tipo entero. La cátedra prohíbe el patrón porque es
una de las causas más frecuentes de resultados «casi correctos» en los prácticos.
Alcance y excepciones¶
Aplica cuando ambos operandos de / son enteros y el resultado se asigna o
pasa a un contexto flotante. Excepciones: la división entera es correcta si
el resultado se usa como entero (por ejemplo, mitad = total / 2 para índices),
o si se explicita la intención con un cast antes de dividir. Un cast defensivo
aquí no es redundante y no cae bajo 0x301Bh: Prohibición de casts de tipo innecesarios o redundantes.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Literales enteros¶
double tasa = 1 / 2;
printf("%f\n", tasa);Por qué falla: ambos operandos son int, así que 1 / 2 vale 0; luego se
convierte a 0.0. Para obtener 0.5 hay que escribir 1.0 / 2.0.
❌ Contraejemplo 2 — Promedio con suma entera¶
double promedio(const int datos[], size_t n)
{
int suma = 0;
for (size_t i = 0; i < n; i++) {
suma += datos[i];
}
return suma / n;
}Por qué falla: suma y n son enteros, de modo que el promedio trunca. Si
suma vale 7 y n vale 2, el retorno es 3.0 en lugar de 3.5.
✅ Ejemplo conforme 1 — Literal flotante y cast¶
double tasa = 1.0 / 2.0;
double promedio(const int datos[], size_t n)
{
int suma = 0;
for (size_t i = 0; i < n; i++) {
suma += datos[i];
}
return (double)suma / (double)n;
}El literal 1.0 y los cast promueven la operación a double antes de
dividir, de modo que la parte fraccionaria sobrevive.
✅ Ejemplo conforme 2 — División entera deliberada¶
int mitad_entera(int total)
{
return total / 2;
}
double mitad_real(int total)
{
return (double)total / 2.0;
}mitad_entera usa / para calcular un índice: la truncación es la intención y
el resultado es entero. mitad_real documenta su intención con el cast. Al
llamador le queda claro cuál es cuál.
⚠️ Casos límite¶
Truncación hacia cero: en C11
-7 / 2es-3, no-4; si esperás redondeo hacia abajo,-7 / 2no sirve.Acumulador entero: conviene convertir una sola vez al final (
(double)suma / n) en lugar de promover cada sumando.floatvsdouble:1 / 2sigue siendoint; el tipo destino no influye. Usá1.0f / 2.0fsi querésfloat.Mezcla con
size_t:suma / nconnde tiposize_tsigue siendo entero; el cast debe ir en el numerador.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Reporta 0x5009h al detectar / entre enteros en contexto flotante. |
gcc / clang | gcc -std=c11 -Wall -Wextra -Werror -pedantic -Wconversion archivo.c | Con -Wconversion aparece conversion from 'int' to 'double' may change value. |
cppcheck | cppcheck --enable=warning archivo.c | Division by zero o advertencias de promoción. |
Checklist de autocontrol¶
¿Al menos un operando de
/esdoubleofloat?¿Usé
1.0en lugar de1cuando quería una división real?¿El cast va en el numerador y no en el resultado ya dividido?
Cuando quise truncar, ¿dejé el resultado como entero?
¿Probé el caso
suma / ncon valores que den fracción?
Reglas relacionadas¶
0x301Bh: Prohibición de casts de tipo innecesarios o redundantes — no elimines este cast: no es redundante, cambia la semántica.
0x3010h: Las variables que representan tamaños o índices de arreglos deben ser de tipo size_t — usá
size_tparan; igualmente hace falta el cast.0x0101h: Los identificadores deben ser descriptivos — nombres claros (
mitad_entera/mitad_real) hacen visible la intención.
Antipatrón: División entera silenciosa asignada a flotante¶
Síntoma en el código del estudiante¶
Se calcula un cociente con dos operandos int y el resultado se guarda en
un float o double, o se imprime con %f:
float tasa = 1 / 2;
double promedio = suma / cantidad;
double porcentaje = aciertos / total * 100;El código parece correcto porque el destino es flotante. El error está en los operandos, que son enteros.
Diagnóstico¶
Mecanismo del defecto¶
En C, el tipo del resultado de a / b se decide antes de la asignación,
según las conversiones aritméticas usuales (C11 §6.3.1.8): si ambos
operandos son enteros, la operación es división entera y descarta la
parte fraccionaria truncando hacia cero (C11 §6.5.5p5). Recién después el
resultado entero se convierte al tipo flotante del destino. Por eso
1 / 2 produce 0 (no 0.5) y recién entonces se promueve a 0.0f: la
información ya se perdió cuando se convierte.
Es un error de tipo semántico: no hay advertencia obligatoria, el compilador
ve una conversión int -> float perfectamente legal.
Consecuencia observable¶
1 / 2almacena0.0;3 / 2almacena1.0, no1.5.Los promedios y porcentajes salen siempre redondeados hacia abajo, y a veces en
0.0si el numerador es menor que el denominador.El bug es silencioso: la salida es numéricamente plausible y solo se nota al comparar contra el resultado esperado.
Fundamento en el estándar C11¶
El estándar define la división entera como truncamiento hacia cero (C11 §6.5.5p5). Para obtener división real, al menos un operando debe ser de tipo flotante antes de la operación, de modo que la promoción ocurra primero y el cociente conserve los decimales. La cátedra prohíbe confiar en la conversión implícita porque oculta la intención y produce resultados difíciles de rastrear.
La división entera por cero, además, es comportamiento indefinido
(C11 §6.5.5p5): no es una excepción controlada y en general termina en
SIGFPE.
Corrección idiomática¶
❌ Código con el antipatrón¶
#include <stdio.h>
double calcular_promedio(int suma, int cantidad)
{
double promedio = suma / cantidad;
return promedio * 100.0;
}✅ Código refactorizado¶
#include <stdio.h>
double calcular_promedio(int suma, int cantidad)
{
if (cantidad == 0) {
return 0.0;
}
double promedio = (double)suma / (double)cantidad;
return promedio * 100.0;
}Alternativas equivalentes y claras: usar un literal flotante (suma / 100.0) o declarar la variable acumuladora como double desde el comienzo.
El cast explícito documenta la intención y evita la división entera.
Errores típicos al compilar o ejecutar¶
Con advertencias de conversión, un caso cercano sí se detecta:
aviso.c:4:29: warning: conversion from 'int' to 'float' may change value [-Wconversion]Con una división por cero literal:
aviso.c:4:29: warning: division by zero [-Wdiv-by-zero]En ejecución, la división entera por cero no controlada aborta con
Floating point exception (core dumped).
Checklist de verificación¶
¿Al menos uno de los operandos de cada división es flotante?
¿Quedó algún
int / intguardado enfloatodouble?¿Usé
(double)explícito o literales como1.0?¿Verifiqué que el divisor no sea
0antes de dividir?¿Contrasté los resultados con casos como
1/2,3/2y5/2?
Reglas relacionadas¶
0x5009h: Prohibición de división entera no intencional asignada a flotantes — prohíbe la división entera no intencional a flotante.
0x301Bh: Prohibición de casts de tipo innecesarios o redundantes — prohíbe casts redundantes; acá el cast sí es necesario.
0x301Ah: Validador de uso idiomático de tipos booleanos estándar — usá tipos booleanos estándar para las guardas.