Respuestas:
Esto es lo que encontré cuando tuve esta duda.
mysql> create table numbers (a decimal(10,2), b float);
mysql> insert into numbers values (100, 100);
mysql> select @a := (a/3), @b := (b/3), @a * 3, @b * 3 from numbers \G
*************************** 1. row ***************************
@a := (a/3): 33.333333333
@b := (b/3): 33.333333333333
@a + @a + @a: 99.999999999000000000000000000000
@b + @b + @b: 100
El decimal hizo exactamente lo que se supone que debe hacer en estos casos, truncó el resto, perdiendo así la parte 1/3.
Entonces, para las sumas, el decimal es mejor, pero para las divisiones, el flotador es mejor, hasta cierto punto, por supuesto. Quiero decir, usar DECIMAL no te dará una "aritmética a prueba de fallas" de ninguna manera.
Espero que esto ayude.
@a
dando 99.999999999000000000000000000000 el DECIMAL? Lo cual es técnicamente correcto.
Un "flotante" en la mayoría de los entornos es un tipo de punto flotante binario. Puede almacenar con precisión los valores de base 2 (hasta cierto punto), pero no puede almacenar con precisión muchos valores de base 10 (decimales). Los flotadores son los más apropiados para cálculos científicos. Están no apropiado para la mayoría de matemáticas orientado a los negocios, y el uso inadecuado de los flotadores te morderá. Muchos valores decimales no se pueden representar exactamente en base-2. 0.1
no puede, por ejemplo, y entonces ves resultados extraños como 1.0 - 0.1 = 0.8999999
.
Los decimales almacenan números de base 10. El decimal es un buen tipo para la mayoría de las matemáticas de negocios (pero cualquier tipo de "dinero" incorporado es más apropiado para los cálculos financieros), donde el rango de valores excede el proporcionado por los tipos enteros, y se necesitan valores fraccionarios. Los decimales, como su nombre lo indica, están diseñados para números de base 10: pueden almacenar con precisión los valores decimales (nuevamente, hasta cierto punto).
MySQL cambió recientemente su forma de almacenar el tipo DECIMAL . En el pasado, almacenaban los caracteres (o nybbles) para cada dígito que comprende una representación ASCII (o nybble) de un número - vs - un entero del complemento a dos, o alguna derivada del mismo.
El formato de almacenamiento actual para DECIMAL es una serie de enteros de 1,2,3 o 4 bytes cuyos bits se concatenan para crear un número de complemento a dos con un punto decimal implícito, definido por usted, y almacenado en el esquema de base de datos cuando declara la columna y especifique su tamaño DECIMAL y la posición del punto decimal.
A modo de ejemplo, si toma un int de 32 bits, puede almacenar cualquier número entre 0 y 4,294,967,295. Eso solo cubrirá de manera confiable 999,999,999, por lo que si arroja 2 bits y usa (1 << 30 -1) no renunciaría a nada. Cubrir todos los números de 9 dígitos con solo 4 bytes es más eficiente que cubrir 4 dígitos en 32 bits utilizando 4 caracteres ASCII u 8 dígitos nybble. (un nybble es de 4 bits, lo que permite valores 0-15, más de lo necesario para 0-9, pero no puede eliminar ese desperdicio yendo a 3 bits, porque eso solo cubre los valores 0-7)
El ejemplo utilizado en los documentos en línea de MySQL utiliza DECIMAL (18,9) como ejemplo. Esto es 9 dígitos delante y 9 dígitos detrás del punto decimal implícito, lo que, como se explicó anteriormente, requiere el siguiente almacenamiento.
Como 18 caracteres de 8 bits: 144 bits
Como 18 nybbles de 4 bits: 72 bits
Como 2 enteros de 32 bits: 64 bits
Actualmente, DECIMAL admite un máximo de 65 dígitos, como DECIMAL (M, D), donde el valor más grande para M permitido es 65, y el valor más grande de D permitido es 30.
Para no requerir fragmentos de 9 dígitos a la vez, se usan enteros menores de 32 bits para agregar dígitos usando enteros de 1,2 y 3 bytes. Por alguna razón que desafía la lógica, se usaron entradas firmadas, en lugar de no firmadas, y al hacerlo, se desecha 1 bit, lo que da como resultado las siguientes capacidades de almacenamiento. Para las entradas de 1,2 y 4 bytes, el bit perdido no importa, pero para el int de 3 bytes es un desastre porque se pierde un dígito completo debido a la pérdida de ese único bit.
Con un int de 7 bits: 0-99
Con un int de 15 bits: 0 - 9,999
Con un int de 23 bits: 0 - 999,999 (0 - 9,999,999 con un int de 24 bits)
Los enteros de 1,2,3 y 4 bytes se concatenan juntos para formar un "conjunto de bits" que DECIMAL usa para representar el número precisamente como un entero de complemento a dos. El punto decimal NO se almacena, está implícito.
Esto significa que no se requieren conversiones ASCII a int del motor DB para convertir el "número" en algo que la CPU reconoce como un número. Sin redondeo, sin errores de conversión, es un número real que la CPU puede manipular.
Los cálculos sobre este entero arbitrariamente grande deben realizarse en software, ya que no hay soporte de hardware para este tipo de número, pero estas bibliotecas son muy antiguas y están muy optimizadas, ya que se escribieron hace 50 años para admitir datos de punto flotante de precisión arbitraria IBM 370 Fortran . Todavía son mucho más lentos que el álgebra de enteros de tamaño fijo realizado con hardware de CPU de enteros, o los cálculos de coma flotante realizados en la FPU.
En términos de eficiencia de almacenamiento, debido a que el exponente de un flotante está unido a todos y cada uno de ellos, especificando implícitamente dónde está el punto decimal, es enormemente redundante y, por lo tanto, ineficiente para el trabajo de DB. En una base de datos ya sabe dónde debe ir el punto decimal al frente, y cada fila de la tabla que tiene un valor para una columna DECIMAL solo necesita mirar la especificación 1 y única de dónde se colocará, almacenará ese punto decimal. en el esquema como los argumentos a un DECIMAL (M, D) como la implicación de los valores M y D.
Los muchos comentarios que se encuentran aquí acerca de qué formato se utilizará para varios tipos de aplicaciones son correctos, por lo que no voy a expresar el punto. Me tomé el tiempo para escribir esto aquí porque quien mantiene la documentación en línea vinculada de MySQL no comprende nada de lo anterior y después de rondas de intentos cada vez más frustrantes de explicárselo, me di por vencido. Una buena indicación de lo poco que entendieron lo que estaban escribiendo es la presentación muy confusa y casi indescifrable del tema.
Como pensamiento final, si necesita un cálculo de punto flotante de alta precisión, ha habido enormes avances en el código de punto flotante en los últimos 20 años, y el soporte de hardware para flotante de precisión cuádruple y 96 bits está a la vuelta de la esquina, pero existen buenas bibliotecas de precisión arbitraria si la manipulación del valor almacenado es importante.
No solo es específico de MySQL, la diferencia entre los tipos flotante y decimal es la forma en que representan valores fraccionarios. Los tipos de punto flotante representan fracciones en binario, que solo pueden representar valores como {m*2^n | m, n Integers}
. valores como 1/5 no pueden representarse con precisión (sin error de redondeo). Los números decimales son igualmente limitados, pero representan números como {m*10^n | m, n Integers}
. Los decimales aún no pueden representar números como 1/3, pero a menudo ocurre en muchos campos comunes, como las finanzas, que la expectativa es que ciertas fracciones decimales siempre se pueden expresar sin pérdida de fidelidad. Dado que un número decimal puede representar un valor como $0.20
(una quinta parte de un dólar), se prefiere en esas situaciones.
decimal es para cantidades fijas como dinero donde desea un número específico de lugares decimales. Los flotadores son para almacenar ... números de precisión de coma flotante.
Encontré esto útil:
En general, los valores flotantes son buenos para los cálculos científicos, pero no deben usarse para valores financieros / monetarios. Para las matemáticas orientadas a los negocios, use siempre Decimal.
Fuente: http://code.rohitink.com/2013/06/12/mysql-integer-float-decimal-data-types-differences/
mysql> CREATE TABLE num(id int ,fl float,dc dec(5,2));
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO num VALUES(1,13.75,13.75);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO num VALUES(2,13.15,13.15);
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM num WHERE fl = 13.15;
Empty set (0.00 sec)
mysql> SELECT * FROM num WHERE dc = 13.15;
+------+-------+-------+
| id | fl | dc |
+------+-------+-------+
| 2 | 13.15 | 13.15 |
+------+-------+-------+
1 row in set (0.00 sec)
mysql> SELECT SUM(fl) ,SUM(dc) FROM num;
+--------------------+---------+
| SUM(fl) | SUM(dc) |
+--------------------+---------+
| 26.899999618530273 | 26.90 |
+--------------------+---------+
1 row in set (0.00 sec)
mysql> SELECT * FROM num WHERE ABS(fl - 13.15)<0.01;
+------+-------+-------+
| id | fl | dc |
+------+-------+-------+
| 2 | 13.15 | 13.15 |
+------+-------+-------+
1 row in set (0.00 sec)
Tipos de punto flotante (valor aproximado): FLOTANTE, DOBLE
Los tipos FLOAT y DOUBLE representan valores de datos numéricos aproximados . MySQL usa cuatro bytes para valores de precisión simple y ocho bytes para valores de precisión doble.
Para FLOAT, el estándar SQL permite una especificación opcional de la precisión (pero no el rango del exponente) en bits que siguen a la palabra clave FLOAT entre paréntesis. MySQL también admite esta especificación de precisión opcional, pero el valor de precisión se usa solo para determinar el tamaño de almacenamiento. Una precisión de 0 a 23 da como resultado una columna FLOAT de precisión simple de 4 bytes. Una precisión de 24 a 53 da como resultado una columna DOBLE de doble precisión de 8 bytes.
MySQL permite una sintaxis no estándar: FLOAT (M, D) o REAL (M, D) o DOBLE PRECISIÓN (M, D). Aquí, "(M, D)" significa que los valores pueden almacenarse con hasta M dígitos en total, de los cuales D dígitos pueden estar después del punto decimal. Por ejemplo, una columna definida como FLOAT (7,4) se verá como -999.9999 cuando se muestre. MySQL realiza el redondeo al almacenar valores, por lo que si inserta 999.00009 en una columna FLOAT (7,4), el resultado aproximado es 999.0001.
Debido a que los valores de punto flotante son aproximados y no se almacenan como valores exactos, los intentos de tratarlos como exactos en las comparaciones pueden generar problemas. También están sujetos a dependencias de plataforma o implementación.
Para una portabilidad máxima, el código que requiere el almacenamiento de valores de datos numéricos aproximados debe usar FLOTACIÓN o DOBLE PRECISIÓN sin especificación de precisión o número de dígitos.
https://dev.mysql.com/doc/refman/5.5/en/floating-point-types.html
Problemas con los valores de coma flotante
Los números de punto flotante a veces causan confusión porque son aproximados y no se almacenan como valores exactos . Un valor de punto flotante como está escrito en una declaración SQL puede no ser el mismo que el valor representado internamente. Los intentos de tratar los valores de punto flotante como exactos en las comparaciones pueden generar problemas. También están sujetos a dependencias de plataforma o implementación. Los tipos de datos FLOAT y DOUBLE están sujetos a estos problemas. Para columnas DECIMAL, MySQL realiza operaciones con una precisión de 65 dígitos decimales, lo que debería resolver los problemas de imprecisión más comunes.
El siguiente ejemplo usa DOUBLE para demostrar cómo los cálculos que se realizan mediante operaciones de punto flotante están sujetos a errores de punto flotante.
mysql> CREATE TABLE t1 (i INT, d1 DOUBLE, d2 DOUBLE);
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.4 | 21.4 |
| 2 | 76.8 | 76.8 |
| 3 | 7.4 | 7.4 |
| 4 | 15.4 | 15.4 |
| 5 | 7.2 | 7.2 |
| 6 | -51.4 | 0 |
+------+-------+------+
El resultado es correcto. Aunque parece que los primeros cinco registros no deberían satisfacer la comparación (los valores de a y b no parecen ser diferentes), pueden hacerlo porque la diferencia entre los números se muestra alrededor del décimo decimal más o menos, dependiendo de los factores como la arquitectura de la computadora o la versión del compilador o el nivel de optimización. Por ejemplo, diferentes CPU pueden evaluar los números de punto flotante de manera diferente.
Si las columnas d1 y d2 se hubieran definido como DECIMAL en lugar de DOBLE, el resultado de la consulta SELECT habría contenido solo una fila, la última que se muestra arriba.
La forma correcta de hacer una comparación de números en coma flotante es decidir primero una tolerancia aceptable para las diferencias entre los números y luego hacer la comparación con el valor de tolerancia. Por ejemplo, si estamos de acuerdo en que los números de coma flotante deben considerarse iguales si son iguales dentro de una precisión de uno en diez mil (0.0001), la comparación debe 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.4 | 0 |
+------+-------+------+
1 row in set (0.00 sec)
Por el contrario, para obtener filas donde los números son iguales, la prueba debe 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.4 | 21.4 |
| 2 | 76.8 | 76.8 |
| 3 | 7.4 | 7.4 |
| 4 | 15.4 | 15.4 |
| 5 | 7.2 | 7.2 |
+------+------+------+
5 rows in set (0.03 sec)
Los valores de punto flotante están sujetos a dependencias de plataforma o implementación. Suponga que ejecuta las siguientes declaraciones:
CREATE TABLE t1(c1 FLOAT(53,0), c2 FLOAT(53,0));
INSERT INTO t1 VALUES('1e+52','-1e+52');
SELECT * FROM t1;
En algunas plataformas, la instrucción SELECT devuelve inf e -inf. En otros, devuelve 0 y -0.
Una consecuencia de los problemas anteriores es que si intenta crear un esclavo de replicación volcando el contenido de la tabla con mysqldump en el maestro y volviendo a cargar el archivo de volcado en el esclavo, las tablas que contienen columnas de punto flotante pueden diferir entre los dos hosts.
https://dev.mysql.com/doc/refman/5.5/en/problems-with-float.html
Regla dura y rápida
Si todo lo que necesita hacer es sumar, restar o multiplicar los números que está almacenando, DECIMAL es lo mejor.
Si necesita dividir o hacer cualquier otra forma de aritmética o álgebra en los datos, seguramente será más feliz con float. Las bibliotecas de punto flotante, y en los procesadores Intel, el propio procesador de punto flotante, tienen TON de operaciones para corregir, arreglar, detectar y manejar la tormenta de excepciones que se producen al realizar funciones matemáticas típicas, especialmente funciones trascendentales.
En cuanto a la precisión, una vez escribí un sistema de presupuesto que calculaba el porcentaje de contribución de cada una de más de 3,000 cuentas, para 3,600 unidades de presupuesto, por mes al nodo de consolidación de esa unidad, luego basado en esa matriz de porcentajes (3,000 + x 12 x 3,600) Multipliqué las cantidades presupuestadas por los nodos organizacionales más altos hasta los siguientes 3 niveles de los nodos organizacionales, y luego calculé todos los valores (3,000 + 12) para las 3,200 unidades de detalle a partir de eso. Millones y millones y millones de cálculos de coma flotante de doble precisión, cualquiera de los cuales arrojaría la acumulación de todas esas proyecciones en una consolidación de abajo hacia arriba al nivel más alto de la organización.
El error de coma flotante total después de todos esos cálculos fue CERO . Eso fue en 1986, y las bibliotecas de punto flotante de hoy son mucho, mucho mejores de lo que eran en aquel entonces. Intel hace todos sus cálculos intermedios de dobles con una precisión de 80 bits, lo que elimina el error de redondeo. Cuando alguien te dice "es un error de coma flotante", es casi seguro que NO es cierto.
float
(y double
) representa fracciones binarias
decimal
representa fracciones decimales
declare @float as float(10)
declare @Decimal as decimal(10)
declare @Inetger as int
set @float =10.7
set @Decimal =10.7
set @Inetger=@Decimal
print @Inetger
en flotante cuando se establece el valor en entero 10, pero en decimal 11
FLOAT(m,n)
, lleva a dos redondeos; mientras tanto, no proporciona nada de ningún uso.