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.