Tenga en cuenta que la siguiente sección es relevante principalmente para versiones
de MySQL anteriores a 5.0.3. A partir de la versión 5.0.3, MyQL realiza las operaciones
DECIMAL con una precisión de 64 dígitos decimales, lo que debería resolver los problemas de imprecisión más comunes en lo que se refiere a columnas DECIMAL. Para las columnas DOUBLE y FLOAT los problemas siguen porque la inexactitud es la naturaleza
básica de los números en coma flotante.
Los números en coma flotante a veces causan confusión porque no son almacenados
como valores exactos en la arquitectura de la computadora. Lo que puede ver en la pantalla
no es el valor exacto del número. Los tipos de columna FLOAT,
DOUBLE, y DECIMAL son de este tipo. Las
columnas DECIMAL almacenan valores con precisión exacta porque
se representan como cadenas de caracteres, pero los cálculos sobre valores
DECIMAL, en versiones previas a 5.0.3, eran realizadas utilizando operaciones de coma flotante.
El siguiente ejemplo (para versiones anteriores a 5.0.3 de MySQL)
demuestra el problema. Muestra que incluso para los tipos de columna
DECIMAL, los cálculos se realizan utilizando operaciones de coma flotante
que están sujetas al error. (En todas las versiones de MySQL, tendría problemas
similares si reemplazara las columnas DECIMAL con
FLOAT).
mysql> CREATE TABLE t1 (i INT, d1 DECIMAL(9,2), d2 DECIMAL(9,2));
mysql> INSERT INTO t1 VALUES (1, 101.40, 21.40), (1, -80.00, 0.00),
-> (2, 0.00, 0.00), (2, -13.20, 0.00), (2, 59.60, 46.40),
-> (2, 30.40, 30.40), (3, 37.00, 7.40), (3, -29.60, 0.00),
-> (4, 60.00, 15.40), (4, -10.60, 0.00), (4, -34.00, 0.00),
-> (5, 33.00, 0.00), (5, -25.80, 0.00), (5, 0.00, 7.20),
-> (6, 0.00, 0.00), (6, -51.40, 0.00);
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b
-> FROM t1 GROUP BY i HAVING a <> b;
+------+--------+-------+
| i | a | b |
+------+--------+-------+
| 1 | 21.40 | 21.40 |
| 2 | 76.80 | 76.80 |
| 3 | 7.40 | 7.40 |
| 4 | 15.40 | 15.40 |
| 5 | 7.20 | 7.20 |
| 6 | -51.40 | 0.00 |
+------+--------+-------+
El resultado es correcto. Aunque los cinco primeros registros aparentan no poder pasar
la comparación (los valores a y b no parecen
ser diferentes), lo son porque la diferencia entre los dos números se encuentra a partir
del decimal número diez aproximadamente, dependiendo de la arquitectura de la computadora.
A partir de MySQL 5.0.3, usted obtendría únicamente el último registro en el resultado anterior.
El problema no puede ser resulto utilizando funciones como ROUND()
o similares, porque el resultado sigue siendo un número en coma flotante:
mysql> SELECT i, ROUND(SUM(d1), 2) AS a, ROUND(SUM(d2), 2) AS b
-> FROM t1 GROUP BY i HAVING a <> b;
+------+--------+-------+
| i | a | b |
+------+--------+-------+
| 1 | 21.40 | 21.40 |
| 2 | 76.80 | 76.80 |
| 3 | 7.40 | 7.40 |
| 4 | 15.40 | 15.40 |
| 5 | 7.20 | 7.20 |
| 6 | -51.40 | 0.00 |
+------+--------+-------+
Esta es la apariencia que los números en la columna a tienen al mostrarse
más cifras decimales:
mysql> SELECT i, ROUND(SUM(d1), 2)*1.0000000000000000 AS a,
-> ROUND(SUM(d2), 2) AS b FROM t1 GROUP BY i HAVING a <> b;
+------+----------------------+-------+
| i | a | b |
+------+----------------------+-------+
| 1 | 21.3999999999999986 | 21.40 |
| 2 | 76.7999999999999972 | 76.80 |
| 3 | 7.4000000000000004 | 7.40 |
| 4 | 15.4000000000000004 | 15.40 |
| 5 | 7.2000000000000002 | 7.20 |
| 6 | -51.3999999999999986 | 0.00 |
+------+----------------------+-------+
Dependiendo de la arquitectura de su computadora, usted puede que vea o no resultados similares. Diferentes procesadores pueden evaluar los números en coma flotante de manera diferente. Por ejemplo, en algunas máquinas usted podría obtener los resultados “correctos” multiplicando ambos argumentos por 1, como en el siguiente ejemplo.
Aviso: Nunca utilice este método en sus aplicaciones. No es un ejemplo de procedimiento fiable.
mysql> SELECT i, ROUND(SUM(d1), 2)*1 AS a, ROUND(SUM(d2), 2)*1 AS b
-> FROM t1 GROUP BY i HAVING a <> b;
+------+--------+------+
| i | a | b |
+------+--------+------+
| 6 | -51.40 | 0.00 |
+------+--------+------+
La razón por la que el ejemplo precedente parece funcionar es que en una máquina particular donde la prueba fue realizada, la aritmética de coma flotante del procesador redondea los números al mismo valor. No obstante, no hay ninguna regla que diga que un procesador deba hacerlo, así que este método no merece confianza.
La manera adecuada de hacer comparaciones en coma flotante es primero decidir una tolerancia aceptable para las diferencias entre los números y realizar entonces las comparaciones sobre el valor de tolerancia. Por ejemplo, si estamos de acuerdo en que los números en coma flotante deben ser considerados el mismo si están dentro de una precisión de uno sobre diez mil (0.0001), la comparación debería escribirse para encontrar diferencias mayores que el valor de tolerancia:
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1
-> GROUP BY i HAVING ABS(a - b) > 0.0001;
+------+--------+------+
| i | a | b |
+------+--------+------+
| 6 | -51.40 | 0.00 |
+------+--------+------+
1 row in set (0.00 sec)
De manera análoga, para obtener los registros en que los números son iguales, la comparación debería encontrar diferencias dentro del valor de tolerancia:
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1
-> GROUP BY i HAVING ABS(a - b) <= 0.0001;
+------+-------+-------+
| i | a | b |
+------+-------+-------+
| 1 | 21.40 | 21.40 |
| 2 | 76.80 | 76.80 |
| 3 | 7.40 | 7.40 |
| 4 | 15.40 | 15.40 |
| 5 | 7.20 | 7.20 |
+------+-------+-------+
Ésta es una traducción del manual de referencia de MySQL, que puede encontrarse en dev.mysql.com. El manual de referencia original de MySQL está escrito en inglés, y esta traducción no necesariamente está tan actualizada como la versión original. Para cualquier sugerencia sobre la traducción y para señalar errores de cualquier tipo, no dude en dirigirse a mysql-es@vespito.com.

