Punto Flotante (Floating Point)¶
Introducción¶
Los números de punto flotante son fundamentales en programación para representar valores decimales. En C/C++, existen dos tipos principales: float (precisión simple) y double (precisión doble). Aunque ambos almacenan números con decimales, difieren significativamente en precisión, tamaño en memoria y rango de valores que pueden representar.
Float vs Double: Comparación Básica¶
Float (Precisión Simple)¶
- Tamaño: 4 bytes (32 bits)
- Precisión: Hasta 7 decimales sin pérdida de precisión
- Rango: 3.4 × 10⁻³⁸ a 3.4 × 10³⁸
- Especificador de formato:
%f - Identificador literal:
f(ejemplo:789.123456f)
Double (Precisión Doble)¶
- Tamaño: 8 bytes (64 bits)
- Precisión: Hasta 15 decimales sin pérdida de precisión
- Rango: 1.7 × 10⁻³⁰⁸ a 1.7 × 10³⁰⁸
- Especificador de formato:
%lf - Identificador literal: Sin sufijo (por defecto son double)
Diferencias de Precisión¶
Un ejemplo práctico muestra claramente la diferencia:
float f = 789.123456f; // Se redondea a 789.123474
double d = 789.123456; // Se mantiene como 789.123456
El float pierde precisión en los últimos dígitos debido a su limitación de bits. Cuando trabajamos con valores decimales sin especificar el tipo, C los toma como double por defecto. Para forzar a float, debe agregarse la letra f al final del literal.
Representación en Memoria (IEEE 754)¶
C sigue el estándar IEEE 754 para representar números de punto flotante en memoria. A diferencia de los enteros que se almacenan directamente en binario, los números flotantes se dividen en componentes específicos.
Componentes de Representación IEEE 754¶
Todos los números flotantes (float o double) constan de tres partes:
- Bit de Signo (Sign Bit): El bit más significativo (MSB)
0= número positivo-
1= número negativo -
Exponente Sesgado (Biased Exponent): Determina la magnitud del número
- No se almacena directamente porque el exponente puede ser negativo o positivo
-
Se suma un sesgo (bias) para convertirlo en positivo
-
Mantisa Normalizada (Mantissa/Significand): Los bits de precisión del número
- Representa la parte fraccionaria del número en notación científica
Float - Estructura de 32 bits¶
| Componente | Bits | Total |
|---|---|---|
| Signo | 1 | 1 bit |
| Exponente | 8 | 8 bits |
| Mantisa | 23 | 23 bits |
| Total | - | 32 bits |
Sesgo para float: 127
Estructura visual:
Double - Estructura de 64 bits¶
| Componente | Bits | Total |
|---|---|---|
| Signo | 1 | 1 bit |
| Exponente | 11 | 11 bits |
| Mantisa | 52 | 52 bits |
| Total | - | 64 bits |
Sesgo para double: 1023
Estructura visual:
Proceso de Conversión: Decimal → Flotante → Hexadecimal¶
Para entender cómo se almacena un número decimal en memoria como número de punto flotante, seguimos estos pasos:
Ejemplo: Convertir 789.123456 a Float¶
Paso 1: Convertir a Binario¶
Dividimos el número en parte entera y parte decimal:
- 789 (entero) en binario:
1100010101 - 0.123456 (decimal) en binario:
0001111110010111...
Combinado: 1100010101.0001111110010111...
Paso 2: Normalizar a Notación Científica Binaria¶
La notación científica binaria requiere que el número esté en la forma 1.XXXXX × 2^n
Corremos el punto decimal hasta tener un 1 como primer dígito:
Se desplazó 9 lugares, por lo que el exponente es 9.
Paso 3: Extraer Componentes¶
- Signo: 0 (es positivo)
- Exponente (sin sesgo): 9
- Exponente sesgado (para float): 9 + 127 = 136
- Exponente sesgado en binario:
10001000 - Mantisa: Tomamos los bits después del punto:
10001010000111111...(rellenado a 23 bits)
Paso 4: Armar el Número en Binario¶
Combinado en 32 bits (sin espacios):
Paso 5: Convertir a Hexadecimal¶
Agrupamos en bloques de 4 bits:
Resultado: 0x4445FE00 (aproximadamente, puede variar según el redondeo)
Conversión de Double¶
El proceso es idéntico, pero con diferencias en los tamaños:
- Sesgo: 1023 (en lugar de 127)
- Exponente: 11 bits
- Mantisa: 52 bits
Para el mismo número 789.123456 como double:
Inspección en Bajo Nivel¶
Usando IDA o Debugger¶
Cuando se trabaja a nivel de ensamblador, los registros XMM (registros de punto flotante) almacenan estos valores. Usando herramientas como IDA Pro o Visual Studio Debugger, podemos:
- Ver el valor en hexadecimal: Nos muestra exactamente cómo se representa en memoria
- Ver el valor como float/double: La herramienta convierte automáticamente al decimal
- Cambiar la visualización: Podemos cambiar entre tipos de datos en el inspector
Instrucciones Comunes de Punto Flotante¶
| Instrucción | Operación |
|---|---|
CVTSS2SD |
Convertir Single (float) a Double |
MOVSD |
Mover Double |
MOVSS |
Mover Single (float) |
ADDSD |
Sumar Double |
MULSS |
Multiplicar Single (float) |
Extracción de Componentes en Código¶
En C, podemos extraer los componentes de un float usando operaciones de bits:
// Obtener el bit de signo
int sign = (value >> 31) & 1;
// Obtener el exponente (8 bits)
int exponent = (value >> 23) & 0xFF;
// Obtener la mantisa (23 bits)
int mantissa = value & 0x7FFFFF;
Para double, los rangos son diferentes:
// En un double (64 bits)
int sign = (value >> 63) & 1;
int exponent = (value >> 52) & 0x7FF; // 11 bits
long long mantissa = value & 0xFFFFFFFFFFFFF; // 52 bits
Perdida de Precisión¶
Por Qué Ocurre¶
El número de bits limitado en la mantisa restringe cuántos dígitos significativos se pueden almacenar con exactitud:
- Float: 23 bits de mantisa = aproximadamente 7-8 dígitos significativos
- Double: 52 bits de mantisa = aproximadamente 15-17 dígitos significativos
Si un número tiene más dígitos significativos que los que caben en la mantisa, se debe redondear.
Ejemplo Práctico¶
Recomendaciones¶
- Usa float cuando el espacio en memoria es limitado y la precisión puede comprometerse
- Usa double para cálculos científicos, financieros o que requieran alta precisión
- Por defecto, siempre es double a menos que especifiques
f
Tabla Comparativa Detallada¶
| Característica | Float | Double |
|---|---|---|
| Precisión | 7 decimales | 15 decimales |
| Tamaño | 32 bits (4 bytes) | 64 bits (8 bytes) |
| Rango | 3.4 × 10⁻³⁸ a 3.4 × 10³⁸ | 1.7 × 10⁻³⁰⁸ a 1.7 × 10³⁰⁸ |
| Especificador | %f |
%lf |
| Bits de signo | 1 | 1 |
| Bits de exponente | 8 | 11 |
| Bits de mantisa | 23 | 52 |
| Sesgo del exponente | 127 | 1023 |
| Literal | valor_f |
valor |
Notas Importantes para Reverse Engineering¶
-
Los registros XMM: Las operaciones de punto flotante usan registros XMM (SSE/AVX), no los registros de propósito general
-
Conversiones automáticas: Cuando ves
CVTSS2SD, se está convirtiendo de float a double. El compilador puede hacer esto automáticamente -
Visualización en herramientas: IDA y debuggers modernos pueden mostrar automáticamente valores hex como float/double
-
Tamaños en ensamblador:
dword= 32 bits = float-
qword= 64 bits = double -
Cálculos manuales: No necesitas memorizar la conversión. Las herramientas hacen el trabajo, pero entender el proceso te ayuda a comprender qué está sucediendo en bajo nivel
Referencias¶
- IEEE 754 Standard - GeeksforGeeks
- IEEE 754: Standard for Floating-Point Arithmetic
- C Language Specification (C99/C11)