Diferencia entre tipo de datos flotante y decimal


175

¿Qué diferencia hay cuando uso tipos de datos flotantes y decimales en MySQL?

¿Cuándo debo usar cuál?


No lo use FLOAT(m,n), lleva a dos redondeos; mientras tanto, no proporciona nada de ningún uso.
Rick James

Respuestas:


185

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.


44
Una excelente prueba. Hace años, las funciones de conversión de datos de C lib a menudo creaban toneladas de pequeñas diferencias en los valores convertidos de ASCII a flotante en comparación con los de, por ejemplo, SQLServer. Esto ya casi nunca es cierto. La prueba es la mejor política, ya que es mejor saber con certeza cuáles son las compensaciones.

15
En realidad, la adición DECIMAL es un error. Si sumas 33.333333333 tres veces no obtienes 100. Si divides 100 por 3, no obtienes un número racional sin un conjunto repetitivo de dígitos finales, por lo que no puedes multiplicarlo por 3 y obtener 100. Sal una calculadora y pruébalo. Lógicamente sabemos que 1/3 + 1/3 + 1/3 debería ser igual a 3 / 3rds IE: 1, pero esta clase de números racionales no nos permite hacer esto. La respuesta flotante es correcta, pero su contador la ODIA. !
user2548100

55
¿No está @adando 99.999999999000000000000000000000 el DECIMAL? Lo cual es técnicamente correcto.
Vincent Poirier

78

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.1no 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).


@Michael Petrotta: el usuario solo ingresa sus números decimales en el campo dado en los formularios ... solo necesito almacenarlos en DB. Que será más adecuado. ?
Hacker

12
@Pradeep: Siento que no estás respondiendo mis preguntas. Esto puede deberse a que usted mismo no conoce las respuestas, tal vez no se sienta cómodo pidiéndole más detalles a su gerente o cliente. Si ese es el caso, le sugiero que muerda la bala, se siente con ellos durante un par de horas y realmente analice su solicitud. ¿Para qué exactamente y con gran detalle se utilizan sus datos?
Michael Petrotta

1
En realidad, actualmente tanto float como DECIMAL almacenan sus números de la misma manera. La diferencia está en cómo se usan esos números. DECIMAL utiliza todos los bits para comprender un entero de complemento a dos, con un punto decimal implícito. Un flotador tiene dos enteros y uno eleva al otro a una potencia. Tanto la base como el exponente son enteros complementarios de dos.
user2548100

1
Creo que su respuesta puede ser técnicamente correcta, pero el énfasis en flotar como un tipo binario oculta el punto de que ambos almacenan sus datos en el mismo formato. Un número de coma flotante elevado a la primera potencia es un número entero y se almacena exactamente de esa manera. De hecho, para el flotante de precisión de 80 bits, la base es un int64. Por el contrario, si escribiera una biblioteca para enteros que los elevara a poderes, encontraría los mismos problemas con enteros, o DECIMALES, o números romanos, o piruletas. No es el almacenamiento lo que está creando los "errores de redondeo", es el manejo de las matemáticas de la biblioteca.
user2548100

1
Dada la muy mala calidad de la pregunta, donde prácticamente no se dan parámetros para indicar cuáles son las áreas de interés de los PO, es difícil saber cuál es la respuesta adecuada. En general, DECIMAL almacenará números más grandes y las librerías de matemáticas cumplen con las expectativas de los contadores, mientras que la doble flotación es un medio de almacenamiento menos eficiente que ha optimizado masivamente las librerías de matemáticas, que satisfacen mucho mejor las expectativas de los científicos y las finanzas (no los contadores).
user2548100

21

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.


Creo que, a partir de la arquitectura Hazwell de Intel, hay operaciones AVX-2 en enteros de 256 bits, que cubren todos los valores posibles que podrían representar 77 dígitos, que podrían usarse para operar directamente en los enteros de precisión extendida de DECIMAL. Puede resultar prudente que Oracle admita una nueva forma de DECIMAL en el futuro que abarque 77 dígitos frente a 65. Estimaría una mejora del rendimiento de 5-10X utilizando hardware en lugar de software. 2 ^ 256 = 115,792,089,237,316,195,423,570,985,008,687,907,853,269,984,665,640,564,039,457,584,007,913,129, 639,936 (78 dígitos)

Los procesadores vectoriales de Intel ahora admiten operaciones matemáticas de 512 bits. Esto cubrirá 154 dígitos. 2 ^ 512 = 13.407.807.929.942.597.099.574.024.998.205.846.127.479.365.820.592.393.377.723.561.443.721.764.030.073.546.976.801.874.298.166.903.427.690.031.858.186.486.050.853.753.882.811.946.569.946.433.649.006.084.096 (155 dígitos)

13

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.


Debido a que los procesadores Intel realizan todas las operaciones intermedias de doble flotación en una precisión de 80 bits, casi sin excepción no hay error de redondeo cuando el resultado final se recorta de 80 bits a 64 bits. Incluso muchas bibliotecas de software de coma flotante pueden manejar estas y cientos de otras anomalías aritméticas. La teoría y la práctica son, por lo tanto, muy divergentes en esta área.

9

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.



5
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)

2

Si busca rendimiento y no precisión, debe tener en cuenta que los cálculos con flotadores son mucho más rápidos que los decimales


2

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


0

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.


-2

float(y double) representa fracciones binarias

decimal representa fracciones decimales


-2
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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.