Diferencia entre double, float y decimal en C#

Video sobre la comparación entre double vs float vs decimal

Conceptos que hay que saber para entender la diferencia entre float, double y decimal

Precisión

Se refiere a la cercanía entre dos o más medidas. Por ejemplo, si mides algo 5 veces y obtienes exactamente 6.32 en cada medición, entonces dicha medición es muy precisa.

Exactitud

Se refiere a la cercanía de un valor a un estándar, o a un valor conocido. Por ejemplo, si mides algo y encuentras que tiene 1 metro de largo, pero el valor conocido para el objeto es de 5 metros, entonces la medida no es muy exacta.

Precisión Aritmética

Se refiere al número de dígitos utilizados para representar un número. Por ejemplo, el número 6.34 es menos preciso aritméticamente que el número 6.3423342.

Con estos conceptos en mente, debemos saber que regularmente, necesitaremos sistemas exactos. No podemos simplemente, aceptar que 6 * 8 es = 25, cuando en realidad sabemos que es igual a 48. Imagina una máquina haciendo este tipo de cálculos erróneos, definitivamente, el mundo caería en un caos.

Sin embargo, existe un pero, al hablar de precisión, y es que, en cualquier sistema, entre mayor sea la precisión, menor será el rendimiento, y entre menor sea la precisón, mayor será el rendimiento.

¿Cuándo usar el Tipo de dato decimal?

Debe ser utilizado en aplicaciones de alta precisión, como por ejemplo, las financieras, o aquellas donde se hagan cálculos que requieran suma precisión.

¿Cuándo usar el Tipo de dato float?

Puede usarse en situaciones en las que puedan existir errores de redondeo, y requieran el uso intensivo de procesamiento, como el dibujo de gráficas o juegos.

¿Cuándo usar el Tipo de dato double?

Puede usarse en situaciones que no requieran el uso de dinero, por ejemplo, en la creación de aplicaciones con gráficos, por ejemplo, videojuegos, y que los mismos requieran de una mayor precisión que un tipo de dato float.

¿Cuál es la diferencia entre un float y un double? (float vs double en C#)

Como el nombre lo indica, el tipo de dato double tiene una precisión doble que el tipo de dato float (2x). En general, un double tiene 15 dígitos decimales de precisión, mientras que el tipo de dato float tiene solamente 7.

Debido a la menor precisión de un tipo de dato float, puede existir pérdida de información más rápidamente, por ejemplo, en el siguiente caso donde trabajamos con tipos de datos flotantes:

float a = 1.f / 81;
float b = 0;
for (int i = 0; i < 729; ++ i)
    b += a;
printf("%.7g\n", b); // Imprimiría 9.000023 

mientras que con variables con tipo de datos double, pasa lo siguiente:

double a = 1.0 / 81;
double b = 0;
for (int i = 0; i < 729; ++ i)
    b += a;
printf("%.15g\n", b); // Imprime 8.99999999999996 

¿Cuál es la diferencia entre un tipo de dato double y un decimal? (double vs decimal en C#)

Aunque el tipo de dato double es bastante preciso, existe un tipo de dato aún más preciso, que es el tipo de dato decimal. Mientras que el tipo de dato double tiene entre 15 – 16 dígitos de precisión, el tipo de dato decimal tiene entre 28 – 29 dígitos de precisión. Se recomienda el tipo de dato decimal para el uso de cantidades monetarias, es decir, si se desean realizar cálculos que la gente normalmente haría a mano con resultados precisos.

Por otra parte, si el valor exacto no es tan importante (como las coordenadas de un sprite en un videojuego), entonces lo ideal sería utilizar double por cuestiones de velocidad.

¿Cómo se declaran un tipo de dato float, double y decimal?:

//Decimal
decimal numDecimal = 12.12m;

//Flotante
float numFloat = 12.12f;

//Doble
double numDouble = 12.12d; 

Ejemplos prácticos sobre la diferencia entre float, double y decimal

Error acumulativo en los tipos de dato double y float

El primer ejemplo que veremos, es uno muy curioso. Examinemos el código fuente.

        public static void DoubleAddition()
        {
            Double x = .1;
            Double result = 10 * x;
            Double result2 = x + x + x + x + x + x + x + x + x + x;

            Console.WriteLine("{0} - {1}", result, result2);
        } 

Podemos apreciar, que tenemos 3 variables, todas ellas del tipo double.

En la segunda variable, llamada result, multiplicamos 10 por el valor de la variable x.

En la tercer variable, llamada result2, llevamos a cabo la misma operación aritmética, sólo que esta vez, sumamos los valores de x 10 veces. Podríamos pensar a primera vista, cuál será el resultado, sin embargo, mira qué pasa cuando ejecutamos la aplicación.

float o decimal o double

Esto tiene una explicación.

En la mayoría de los sistemas, un número como 0.1 no puede ser representado exactamente utilizando bases binarias. Siempre va a existir algún tipo de error en la precisión aritmética cuando utilicemos este tipo de números.

Se dice que el error de precisión aritmético no es muy notorio cuando se hacen operaciones matemáticos, sin embargo, mientras más operaciones se realicen, más notorio va a ser este error.

La causa de que veamos 2 números diferentes en la consola, es porque en la primer asignación, sólo ejecutamos una única operación, mientras que en la segunda, realizamos 10 operaciones, lo que va acumulando el error en cada operación.

Rendimiento vs Exactitud en los tipos de datos float, double y decimal

Aquí tenemos otro ejercicio práctico e interesante. En esta ocasión, crearemos un par de métodos, que realizarán una serie de operaciones sencillas. Ejecutaremos un cálculo cien millones de veces, y veremos el rendimiento, tanto cuando usamos un tipo de dato double, como cuando utilizamos un tipo de dato decimal. Este es el código de las funciones.

        private static void DoubleTest()
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            float z = 0;
            for (int i = 0; i < iterations; i++)
            {
                float x = i;
                float y = x * i;
                z += y;
            }
            watch.Stop();
            Console.WriteLine("Double: " + watch.ElapsedTicks);
            Console.WriteLine(z);
        }

        private static void DecimalTest()
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            Decimal z = 0;
            for (int i = 0; i < iterations; i++)
            {
                Decimal x = i;
                Decimal y = x * i;
                z += y;
            }
            watch.Stop();
            Console.WriteLine("Decimal: " + watch.ElapsedTicks);
            Console.WriteLine(z);
        } 

Una vez que ejecutamos la aplicación, veamos los resultados en la consola que nos son arrojados.

tipos de datos float decimal y double

Podemos apreciar, que mientras que las operaciones con el tipo de dato double, tardan alrededor de 3 millones de ticks de reloj, las operaciones con el tipo de dato decimal tardan alrededor de 30 millones de ticks. ¡Casi 10 veces más!

¿Porqué pasa esto?

Por una sencilla razón, el tipo de dato double, utiliza una base 2, mientras que un tipo de dato decimal usa una base 10.

Una base 2 es mucho más rápido de calcular para una computadora

Podemos concluir entonces, que el tipo de dato double debe utilizarse para cuestiones de rendimiento, mientras que el tipo de dato decimal, para precisión.

Si utilizas un double, debes estar consciente que tendrás una pérdida de precisión, pero ganarás velocidad, mientras que con un decimal, ganarás precisión, pero perderás rendimiento.

Es por ello, que el tipo decimal es tan utilizado para cuestiones de cálculos de dinero.

Pérdida de datos entre los tipos de datos double, float y decimal

Supongamos que somos contratados por un banco para llevar a cabo un sistema que simule el total de dinero resultante de un fondo de inversión de una empresa, durante el lapso de 6 meses, siendo que cada mes se genera un 20% de interés (jaja sí como no).

Pongo el código fuente para que lo hagas tú también:

static void Main()
{

    decimal[] saldosInicialesDecimal = {
        2243829345634598.78m, 2243829345634598.78m, 3357829324337839.22m,
        2341789327220561.78m, 2222257897297333.33m, 3343342345329811.12m,
        1234323227383356.78m, 9876352343224254.32m, 3333748574989322.22m,
        6666444421127655.55m, 2222277382357333.33m, 3389098045329811.12m,
        1234984789323356.78m, 9876354378829254.32m, 3333789273989322.22m,
        6666472347828655.55m, 2222278392357333.33m, 3345392345728811.12m,
        1234322223423356.78m, 9876332113325254.32m, 3332873983789322.22m,
        6666487927827655.55m, 2222233243257333.33m, 3345398739329811.12m
    };
    float[] saldosInicialesFloat = {
        2243829345634598.78f, 2243829345634598.78f, 3357829324337839.22f,
        2341789327220561.78f, 2222257897297333.33f, 3343342345329811.12f,
        1234323227383356.78f, 9876352343224254.32f, 3333748574989322.22f,
        6666444421127655.55f, 2222277382357333.33f, 3389098045329811.12f,
        1234984789323356.78f, 9876354378829254.32f, 3333789273989322.22f,
        6666472347828655.55f, 2222278392357333.33f, 3345392345728811.12f,
        1234322223423356.78f, 9876332113325254.32f, 3332873983789322.22f,
        6666487927827655.55f, 2222233243257333.33f, 3345398739329811.12f
    };
    double[] saldosInicialesDouble = {
        2243829345634598.78d, 2243829345634598.78d, 3357829324337839.22d,
        2341789327220561.78d, 2222257897297333.33d, 3343342345329811.12d,
        1234323227383356.78d, 9876352343224254.32d, 3333748574989322.22d,
        6666444421127655.55d, 2222277382357333.33d, 3389098045329811.12d,
        1234984789323356.78d, 9876354378829254.32d, 3333789273989322.22d,
        6666472347828655.55d, 2222278392357333.33d, 3345392345728811.12d,
        1234322223423356.78d, 9876332113325254.32d, 3332873983789322.22d,
        6666487927827655.55d, 2222233243257333.33d, 3345398739329811.12d
    };

    var saldoTotalDecimal = 0m;
    var saldoTotalFloat = 0f;
    var saldoTotalDouble = 0d;

    foreach (decimal m in saldosInicialesDecimal)
    {
        saldoTotalDecimal += m * .2m;
    }
    foreach (float f in saldosInicialesFloat)
    {
        saldoTotalFloat += f * .2f;
    }
    foreach (double d in saldosInicialesDouble)
    {
        saldoTotalDouble += d * .2d;
    }
    Console.WriteLine($"Decimal: {saldoTotalDecimal}");
    Console.WriteLine($"Float: {saldoTotalFloat}");
    Console.WriteLine($"Double: {saldoTotalDouble}");           
} 

Si vemos los resultados, tenemos lo siguiente:

diferencia float decimal y double

Vemos cómo claramente, con el tipo de dato float, estamos perdiendo demasiada información sobre el saldo total, con el tipo de dato double perdemos menos, pero aún así, si consideramos que el ejemplo se basa en dinero, también estamos perdiendo información sobre la cantidad de dinero resultante. El tipo de dato decimal no pierde información, por lo tanto es más precisa su información. Como curiosidad, si imprimos la información haciendo un cast a decimal, obtendremos cuánta información estamos perdiendo:

float vs decimal vs double

¡Saludos!

2 comentarios en “Diferencia entre double, float y decimal en C#”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

0
    0
    Your Cart
    Your cart is emptyReturn to Shop