1. Introducción
En la vida real, casi nunca te pagan dinero con precisión de cien billonésimas de euro, ni mides la longitud de una habitación hasta las centésimas de milímetro. En programación pasa igual: los resultados con double o float muchas veces son “demasiado decimales”.
Aquí tienes un problema clásico:
double resultado = 10.0 / 3.0;
Console.WriteLine(resultado); // 3.3333333333333335
¿Y si queremos mostrar este número, por ejemplo, solo con dos decimales (para no “asustar” al usuario)? Para eso sirve el redondeo.
2. Métodos principales de redondeo en C#
C# te da varias formas de redondear un número de punto flotante. ¡Y aquí empieza lo interesante! ¡Porque hay varios tipos de redondeo! Vamos a ver las opciones principales y entender en qué se diferencian.
1) Función Math.Round — redondeo clásico
La forma más popular es el método Math.Round. Redondea el número al entero más cercano (o a la cantidad de decimales que quieras).
Math.Round(número[, cantidad_decimales[, MidpointRounding]])
- número — lo que quieres redondear.
- cantidad_decimales — cuántos decimales dejar (por defecto 0, o sea, redondea al entero).
- MidpointRounding — el modo de redondeo de mitades (hablamos de esto en un momento).
Ejemplos:
double x = 2.71828;
Console.WriteLine(Math.Round(x)); // 3
Console.WriteLine(Math.Round(x, 2)); // 2.72
Console.WriteLine(Math.Round(x, 3)); // 2.718
Visualización:
| Valor de entrada | Math.Round(x) | Math.Round(x, 2) |
|---|---|---|
| 2.3 | 2 | 2.3 |
| 2.5 | 2 | 2.5 |
| 2.7 | 3 | 2.7 |
| 2.71828 | 3 | 2.72 |
Tipo de resultado:
Todas las funciones de esta lección devuelven tipo double, así que si quieres asignar el resultado del redondeo, puedes tener un error:
double resultado = 10.0 / 5.0; // Resultado 2.0
int x = Math.Round(x); // ¡Error! No puedes asignar un double directamente a una variable int
Si quieres asignar un double a una variable int, tienes que hacer un cast explícito:
double resultado = 10.0 / 5.0; // Resultado 2.0
int x = (int) Math.Round(x); // ¡Así sí funciona!
2) Redondeo hacia arriba o hacia abajo
C# soporta no solo el redondeo “clásico”, sino también el redondeo forzado hacia arriba o hacia abajo.
Redondeo hacia abajo (Math.Floor)
El método Math.Floor simplemente “corta” la parte decimal (hacia menos infinito). ¡Siempre redondea hacia abajo!
Console.WriteLine(Math.Floor(2.99)); // 2
Console.WriteLine(Math.Floor(-2.99)); // -3
Redondeo hacia arriba (Math.Ceiling)
El método Math.Ceiling es lo contrario de Floor: siempre redondea hacia arriba (hacia más infinito).
Console.WriteLine(Math.Ceiling(2.01)); // 3
Console.WriteLine(Math.Ceiling(-2.01)); // -2
Esquema comparativo:
| Función | 2.3 | -2.3 |
|---|---|---|
| Math.Round | 2 | -2 |
| Math.Floor | 2 | -3 |
| Math.Ceiling | 3 | -2 |
3) Truncate: simplemente cortar la parte decimal
El método Math.Truncate es como un “cirujano tranquilo”. Simplemente elimina la parte decimal, sin preocuparse por la corrección matemática del redondeo. Si x es positivo — el resultado es como Floor, si es negativo — como Ceiling.
Console.WriteLine(Math.Truncate(2.99)); // 2
Console.WriteLine(Math.Truncate(-2.99)); // -2
3. ¿Cómo funciona Math.Round para “mitades”? (MidpointRounding)
Imagina que tienes 2.5. ¿A dónde redondear? Está justo “a mitad de camino” entre 2 y 3. En matemáticas hay dos enfoques populares:
- Redondear hacia arriba (“al siguiente mayor”)
- Redondear al par más cercano (Banker's rounding)
C# por defecto usa el segundo enfoque — redondear al par más cercano (“banker's rounding”, o “redondeo hacia el par”).
Console.WriteLine(Math.Round(2.5)); // 2
Console.WriteLine(Math.Round(3.5)); // 4
¿Por qué? Porque entre dos enteros igual de cercanos, el par “gana”. Esto se hace para que, al redondear muchos números, no haya un error sistemático hacia arriba o hacia abajo. Es muy importante, por ejemplo, al calcular grandes sumas de dinero en un banco (de ahí el nombre).
Si quieres redondear siempre hacia arriba, puedes especificar el modo que quieras:
// Redondear siempre "alejándose de cero"
Console.WriteLine(Math.Round(2.5, 0, MidpointRounding.AwayFromZero)); // 3
Console.WriteLine(Math.Round(-2.5, 0, MidpointRounding.AwayFromZero)); // -3
Tabla de modos de MidpointRounding:
| Valor de entrada | Round (Por defecto) | AwayFromZero | ToZero / ToEven |
|---|---|---|---|
| 2.5 | 2 | 3 | 2 |
| 3.5 | 4 | 4 | 4 |
| -2.5 | -2 | -3 | -2 |
4. Redondear a la cantidad de decimales que quieras: ¡ojo con las “ochos” y las “tres”!
A veces necesitas redondear no al entero, sino, por ejemplo, a dos decimales (por ejemplo, para mostrar dinero o porcentajes).
double precio = 149.9999;
double precioRedondeado = Math.Round(precio, 2);
Console.WriteLine(precioRedondeado); // 150
¿Y si solo quieres “cortar” los decimales extra, pero sin redondear? Por ejemplo, de 123.4567 a 123.45.
Puedes hacerlo con un pequeño truco:
double num = 123.4567;
double resultado = Math.Floor(num * 100) / 100;
Console.WriteLine(resultado); // 123.45
Movemos la coma “a mano”, cortamos la parte decimal, y la devolvemos a su sitio.
5. Formateo de la salida vs redondeo
El formateo de la salida ({x:F2}) — no siempre es "redondeo real", sino solo una “máscara” de cómo se ve el número en pantalla. En memoria sigue siendo un double largo y “con cola”. Si necesitas redondear el valor y guardarlo, usa Math.Round.
Aquí tienes un ejemplo donde la diferencia puede ser crítica:
double valor = 2.555;
Console.WriteLine($"{valor:F2}"); // 2.56
Console.WriteLine(Math.Round(valor, 2)); // 2.56
double guardado = Math.Round(valor, 2);
Console.WriteLine(guardado); // 2.56
// Pero si solo lo muestras con formato, sin redondear...
Console.WriteLine($"{valor:F2}"); // 2.56, pero en memoria — 2.555
6. Errores típicos y matices
- ¡No uses redondeo para cálculos financieros exactos con double! Para dinero en .NET hay un tipo especial decimal — no “juega” con la coma flotante (más detalles en otra lección).
- Las “ochos colgadas” tristes: por el almacenamiento binario, no todos los números se redondean “lógicamente” a simple vista. Por ejemplo, 0.1 + 0.2 puede no ser 0.3.
- ¡No confundas Math.Floor y Math.Round! El primero siempre hacia abajo, el segundo — como manda la matemática.
- Acumulación incontrolada de error: si redondeas mucho los resultados intermedios (por ejemplo, dentro de un bucle), el valor final puede “irse” inesperadamente. Normalmente es mejor redondear solo el resultado final, no cada paso intermedio.
7. Tabla final: ¿qué hace cada método?
| Valor de entrada | Math.Round(x) | Math.Floor(x) | Math.Ceiling(x) | Math.Truncate(x) |
|---|---|---|---|---|
| 3.2 | 3 | 3 | 4 | 3 |
| 3.5 | 4 | 3 | 4 | 3 |
| -3.2 | -3 | -4 | -3 | -3 |
| -3.5 | -4 | -4 | -3 | -3 |
8. Curiosidades
- Una vez la NASA perdió una nave espacial por redondear mal las coordenadas y por la diferencia entre unidades métricas/imperiales. Moraleja: ¡redondea con cabeza!
- Los bancos hace tiempo que usan “banker's rounding” para no perder céntimos — antes, a la antigua, todo se redondeaba hacia arriba y los clientes perdían fracciones de céntimo en cada transferencia.
- En C# incluso hay un método Math.Sign, que te devuelve el signo del número — a veces útil antes de redondear, para decidir tú mismo hacia dónde “cortar”.
GO TO FULL VERSION