CodeGym /Cursos /C# SELF /Problemas de precisión y valores especiales

Problemas de precisión y valores especiales

C# SELF
Nivel 6 , Lección 3
Disponible

1. Introducción

Parece que si el ordenador es "listo", entonces 0.1 + 0.2 debería ser simplemente 0.3. Pero no es exactamente así. Vamos a verlo con un ejemplo sencillo.

double x = 0.1;
double y = 0.2;
double sum = x + y;
Console.WriteLine(sum); // ¿Qué va a mostrar el programa?
Suma de números reales: esperamos 0.3, ¿pero qué sale en realidad?

Ahora intenta comparar con 0.3:

Console.WriteLine(sum == 0.3); // ¿Aquí será true o false?

Si ves False, ¡no te sorprendas!

La razón está en cómo se representan los números en la memoria

Los ordenadores trabajan con números en sistema binario. Pero no todas las fracciones decimales se pueden representar como fracciones binarias finitas, igual que 1/3 no se puede escribir exactamente en decimal (0.333...). Por ejemplo, 0.1 en binario es una fracción infinita, y hay que "redondearla" para guardarla.

Dicho en palabras simples, double a veces "finge" que guarda tu número exactamente, pero en realidad solo almacena un valor muy aproximado.

2. ¿Qué "fallos" pueden pasar con la aritmética de double?

Vamos a ver ejemplos prácticos.

Ejemplo 1. La clásica "magia" de 0.1 + 0.2

double a = 0.1;
double b = 0.2;
double sum = a + b;
Console.WriteLine(sum);            // 0.30000000000000004
Console.WriteLine(sum == 0.3);     // False

El ordenador no mostró 0.3, sino 0.30000000000000004. La diferencia es pequeña, pero si trabajas, por ejemplo, con finanzas — ya es crítico.

Ejemplo 2. Suma iterativa

double result = 0;
for (int i = 0; i < 10; i++)
{
    result += 0.1;
}
Console.WriteLine(result); // 0.9999999999999999

Querías 1.0 — salió un poco menos. Otra vez, la razón es el redondeo dentro de double.

Por qué esto importa en problemas reales

Muchos piensan: "¿Qué más da, es un error pequeño, no pasa nada!" Vamos a ver un ejemplo del mundo de los pagos.

Supón que tu banco online suma 100 transacciones de 0.1 euros cada una. Si tu programa "pierde" una cienmilésima de euro en cada iteración, a escala de banco ya "pierdes" dinero real. Y ahí enseguida viene el contable y te pregunta: "¿Dónde está nuestro dinero?!"

3. Cómo comparar correctamente números reales

Como double muchas veces no puede guardar exactamente el valor que esperas, la comparación directa con == puede fallar. En vez de eso, se suele comparar el valor absoluto de la diferencia con un número muy pequeño (epsilon).

Ejemplo de comparación con tolerancia

double a = 0.1 + 0.2;
double b = 0.3;
double epsilon = 0.000001;

if (Math.Abs(a - b) < epsilon)
{
    Console.WriteLine("¡Casi igual!"); // Así es más seguro comparar
}

Aquí decimos: "Si la diferencia entre los números es menor que una millonésima, consideramos que son iguales".

4. Valores especiales de double: Infinity, NaN, -Infinity

El tipo double no solo guarda números, sino también valores especiales. Aparecen en situaciones que un matemático le prohibiría a cualquier estudiante.

Infinito (Infinity)

¿Qué pasa si divides 1 entre 0?

double result = 1.0 / 0.0;
Console.WriteLine(result); // Infinity

En C# (y en muchos lenguajes) dividir por 0 para double no lanza excepción. En vez de eso, el resultado es el valor especial "infinito positivo".

Menos infinito (-Infinity)

Si divides un número negativo por 0, obtienes infinito negativo:

double result = -1.0 / 0.0;
Console.WriteLine(result); // -Infinity

"No es un número" (NaN — Not a Number)

Si haces algo muy raro, como intentar calcular la raíz de un número negativo:

double result = Math.Sqrt(-1);
Console.WriteLine(result); // NaN

O el resultado de dividir 0.0 / 0.0:

double result = 0.0 / 0.0;
Console.WriteLine(result); // NaN

NaN — es "todo lo que no sería un número en la vida real".

Comprobando valores especiales

En C# hay funciones para comprobar valores especiales:

Console.WriteLine(double.IsInfinity(result));    // true si es infinito
Console.WriteLine(double.IsNaN(result));         // true si es NaN

Tabla: Cómo reacciona double a operaciones raras

Operación Resultado Qué guarda double
1.0 / 0.0
Infinity +∞
-1.0 / 0.0
-Infinity -∞
0.0 / 0.0
NaN No es un número
Math.Sqrt(-1)
NaN No es un número
2
Tarea
C# SELF, nivel 6, lección 3
Bloqueada
Diferencia en la precisión aritmética
Diferencia en la precisión aritmética
2
Tarea
C# SELF, nivel 6, lección 3
Bloqueada
Comparación usando epsilon
Comparación usando epsilon
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION