Diferença entre tipo de dados flutuante e decimal

175

Que diferença faz quando eu uso tipos de dados flutuantes e decimais no MySQL ?.

Quando devo usar qual?

Hacker
fonte
Não use FLOAT(m,n), isso leva a dois arredondamentos; enquanto isso, ele não oferece nada de útil.
21718 Rick Rick

Respostas:

185

Foi isso que descobri quando tive essa dúvida.

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

O decimal fez exatamente o que deveria ser feito nesses casos, truncou o restante, perdendo a parte 1/3.

Portanto, para somas o decimal é melhor, mas para divisões o float é melhor, até certo ponto, é claro. Quero dizer, usar DECIMAL não fornecerá uma "aritmética à prova de falhas" de nenhuma maneira.

Espero que isto ajude.

kanap008
fonte
4
Um excelente teste. Anos atrás, as funções de conversão de dados da biblioteca C costumavam criar toneladas de pequenas diferenças nos valores convertidos do ASCII para flutuar quando comparados aos do SQLServer, por exemplo. Isso raramente é verdade mais. O teste é a melhor política, pois é melhor saber ao certo quais são as compensações.
15
Na verdade, a adição DECIMAL está incorreta. Se você adicionar 33.333333333 três vezes, não receberá 100. Se você dividir 100 por 3, não obterá um número racional sem um conjunto repetido de dígitos à direita, portanto, não poderá multiplicá-lo por 3 e obter 100. Saia uma calculadora e tente. Logicamente, sabemos que 1/3 + 1/3 + 1/3 deve ser igual a 3 / 3rds IE: 1, mas essa classe de números racionais não nos permite fazer isso. A resposta flutuante está correta, mas seu contador o odiará. !
precisa saber é o seguinte
5
Não está @adando 99.999999999000000000000000000000 ao DECIMAL? O que é tecnicamente correto.
Vincent Poirier
78

Um "float" na maioria dos ambientes é do tipo de ponto flutuante binário. Ele pode armazenar com precisão os valores da base 2 (até um determinado ponto), mas não pode armazenar com precisão muitos valores da base 10 (decimal). Os flutuadores são mais apropriados para cálculos científicos. Eles não são adequados para a maioria das matemáticas voltadas para os negócios, e o uso inadequado de carros alegóricos o morderá. Muitos valores decimais não podem ser representados exatamente na base 2. 0.1não pode, por exemplo, e você vê resultados estranhos como 1.0 - 0.1 = 0.8999999.

Os decimais armazenam números da base 10. Decimal é um bom tipo para a maioria das matemáticas comerciais (mas qualquer tipo de "dinheiro" interno é mais apropriado para cálculos financeiros), onde o intervalo de valores excede o fornecido por tipos inteiros e são necessários valores fracionários. Os decimais, como o nome indica, são projetados para números da base 10 - eles podem armazenar com precisão valores decimais (novamente, até um determinado ponto).

Michael Petrotta
fonte
@ Michael Petrotta - usuário apenas digita seus números decimais no campo fornecido em formulários .. eu preciso apenas armazená-los no banco de dados. qual será mais adequado. ?
Hacker
12
@ Pradeep: Eu sinto que você não está respondendo minhas perguntas. Talvez você não saiba as respostas - talvez não se sinta à vontade para solicitar mais detalhes ao seu gerente ou cliente. Se for esse o caso, sugiro que morda a bala, sente-se com eles por algumas horas e realmente passeie pelo aplicativo. Para que exatamente e em grandes detalhes os dados estão sendo usados?
quer
1
Atualmente, tanto float quanto DECIMAL armazenam seus números da mesma maneira. A diferença está em como esses números são usados. DECIMAL usa todos os bits para compreender um número inteiro de complemento de dois, com um ponto decimal implícito. Um flutuador tem dois números inteiros e um eleva o outro a uma potência. Tanto a base quanto o expoente são números inteiros de complemento de dois.
usar o seguinte comando
1
Eu acho que sua resposta pode estar tecnicamente correta, mas a ênfase no float sendo um tipo binário obscurece o ponto em que os dois armazenam seus dados no mesmo formato. Um número de ponto flutuante elevado à primeira potência é um número inteiro e é armazenado exatamente dessa maneira. De fato, para flutuação de precisão de 80 bits, a base é um int64. Por outro lado, se você escrevesse uma biblioteca para números inteiros que os elevassem a potências, encontraria os mesmos problemas com números inteiros, DECIMAIS, Números romanos ou pirulitos. Não é o armazenamento que está criando os "erros de arredondamento", é o tratamento da matemática pela biblioteca.
user2548100
1
Dada a péssima qualidade da pergunta, onde praticamente nenhum parâmetro é fornecido para indicar quais são as áreas de preocupação dos POs, é difícil saber qual é a resposta apropriada. Geralmente, o DECIMAL armazena números maiores e as bibliotecas de matemática atendem às expectativas do contador, enquanto o double float é um meio de armazenamento menos eficiente que otimiza enormemente as bibliotecas de matemática - que atendem muito melhor às expectativas dos cientistas e das finanças (e não aos contadores).
user2548100
21

O MySQL mudou recentemente a maneira como armazenam o tipo DECIMAL . No passado, eles armazenavam os caracteres (ou nybbles) de cada dígito, compreendendo uma representação ASCII (ou nybble) de um número - vs - um número inteiro de complemento de dois, ou alguma derivada do mesmo.

O formato de armazenamento atual para DECIMAL é uma série de números inteiros de 1,2,3 ou 4 bytes cujos bits são concatenados para criar um número de complemento de dois com um ponto decimal implícito, definido por você, e armazenado no esquema DB quando você declara a coluna e especifique o tamanho DECIMAL e a posição do ponto decimal.

A título de exemplo, se você usar um int de 32 bits, poderá armazenar qualquer número de 0 a 4.294.967.295. Isso abrangerá apenas 999.999.999 de maneira confiável; portanto, se você jogou 2 bits e usou (1 << 30 -1), não desistiu de nada. Cobrir todos os números de 9 dígitos com apenas 4 bytes é mais eficiente do que cobrir 4 dígitos em 32 bits usando 4 caracteres ASCII ou 8 dígitos de nybble. (um nybble é de 4 bits, permitindo valores de 0 a 15, mais do que o necessário para 0 a 9, mas você não pode eliminar esse desperdício indo para 3 bits, porque isso cobre apenas os valores de 0 a 7)

O exemplo usado nos documentos online do MySQL usa DECIMAL (18,9) como exemplo. Ele tem 9 dígitos à frente e 9 dígitos atrás do ponto decimal implícito, o que, conforme explicado acima, requer o seguinte armazenamento.

Como 18 caracteres de 8 bits: 144 bits

Como 18 nybbles de 4 bits: 72 bits

Como 2 números inteiros de 32 bits: 64 bits

Atualmente, o DECIMAL suporta um máximo de 65 dígitos, como DECIMAL (M, D), onde o maior valor permitido para M é 65 e o maior valor permitido de D é 30.

Para não exigir pedaços de 9 dígitos por vez, números inteiros menores que 32 bits são usados ​​para adicionar dígitos usando números inteiros de 1,2 e 3 bytes. Por alguma razão que desafia a lógica, foram usadas entradas assinadas em vez de entradas não assinadas e, dessa maneira, 1 bit é descartado, resultando nos seguintes recursos de armazenamento. Para ints de 1,2 e 4 bytes, o bit perdido não importa, mas para os int de 3 bytes é um desastre, porque um dígito inteiro é perdido devido à perda desse bit único.

Com um int de 7 bits: 0 - 99

Com um int de 15 bits: 0 - 9.999

Com um int de 23 bits: 0 - 999.999 (0 - 9.999.999 com um int de 24 bits)

Inteiros de 1,2,3 e 4 bytes são concatenados juntos para formar um "pool de bits" que DECIMAL usa para representar o número precisamente como um número inteiro de complemento de dois. O ponto decimal NÃO é armazenado, está implícito.

Isso significa que não são necessárias conversões ASCII para int do mecanismo de banco de dados para converter o "número" em algo que a CPU reconheça como um número. Sem arredondamentos, sem erros de conversão, é um número real que a CPU pode manipular.

Os cálculos nesse número inteiro arbitrariamente grande devem ser feitos em software, pois não há suporte de hardware para esse tipo de número, mas essas bibliotecas são muito antigas e altamente otimizadas, tendo sido escritas há 50 anos para suportar dados de ponto flutuante de precisão arbitrária do IBM 370 Fortran . Eles ainda são muito mais lentos que a álgebra inteira de tamanho fixo feita com hardware inteiro da CPU ou cálculos de ponto flutuante feitos na FPU.

Em termos de eficiência de armazenamento, como o expoente de um flutuador é anexado a todo e qualquer flutuador, especificando implicitamente onde está o ponto decimal, é massivamente redundante e, portanto, ineficiente para o trabalho do DB. Em um banco de dados, você já sabe onde o ponto decimal deve ser colocado na frente, e todas as linhas da tabela que possuem um valor para uma coluna DECIMAL precisam apenas olhar para a especificação 1 e única de onde esse ponto decimal deve ser colocado, armazenado no esquema como os argumentos para um DECIMAL (M, D) como a implicação dos valores M e D.

As muitas observações encontradas aqui sobre qual formato deve ser usado para vários tipos de aplicativos estão corretas, por isso não abordarei o assunto. Eu dediquei um tempo para escrever isso aqui, porque quem está mantendo a documentação on-line do MySQL não entende nenhuma das opções acima e, após rodadas de tentativas cada vez mais frustrantes de explicá-las, desisti. Uma boa indicação de quão mal eles entenderam o que estavam escrevendo é a apresentação muito confusa e quase indecifrável do assunto.

Como pensamento final, se você precisa de computação de ponto flutuante de alta precisão, houve enormes avanços no código de ponto flutuante nos últimos 20 anos, e o suporte de hardware para o flutuador de 96 bits e de precisão quádrupla está ao virar da esquina, mas existem boas bibliotecas de precisão arbitrárias por aí, se a manipulação do valor armazenado for importante.

user2548100
fonte
Acredito que, na arquitetura Hazwell da Intel, haja operações AVX-2 em números inteiros de 256 bits, cobrindo todos os valores possíveis que 77 dígitos poderiam representar, que poderiam ser usados ​​para operar diretamente nos números inteiros de precisão estendidos da DECIMAL. Pode ser prudente para a Oracle suportar uma nova forma de DECIMAL no futuro, cobrindo 77 dígitos vs 65. Eu estimaria uma melhoria de desempenho de 5 a 10X usando hardware em vez 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)
Os processadores vetoriais da Intel agora suportam operações matemáticas de 512 bits. Isso cobrirá 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

Não apenas específica para o MySQL, a diferença entre os tipos float e decimal é a maneira como eles representam valores fracionários. Os tipos de ponto flutuante representam frações em binário, que podem representar apenas valores como {m*2^n | m, n Integers}. valores como 1/5 não podem ser representados com precisão (sem erro de arredondamento). Os números decimais são igualmente limitados, mas representam números como {m*10^n | m, n Integers}. Os decimais ainda não podem representar números como 1/3, mas geralmente ocorre em muitos campos comuns, como finanças, que a expectativa é que certas frações decimais sempre possam ser expressas sem perda de fidelidade. Como um número decimal pode representar um valor como $0.20(um quinto de um dólar), é preferido nessas situações.

SingleNegationElimination
fonte
Como os processadores Intel realizam todas as operações intermediárias de flutuação dupla com precisão de 80 bits, quase sem exceção não há erro de arredondamento quando o resultado final é reduzido de 80 para 64 bits. Mesmo muitas bibliotecas de software de ponto flutuante podem lidar com essas e centenas de outras anomalias aritméticas. Teoria e prática são, portanto, muito divergentes nessa área.
9

decimal é para quantidades fixas, como dinheiro, nas quais você deseja um número específico de casas decimais. Os flutuadores servem para armazenar ... números de precisão de ponto flutuante.

Skylar Saveland
fonte
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)
zloctb
fonte
2

Se você procura desempenho e não precisão, observe que os cálculos com flutuadores são muito mais rápidos que decimais

Semra
fonte
2

Tipos de ponto flutuante (valor aproximado) - FLOAT, DOUBLE

Os tipos FLOAT e DOUBLE representam valores numéricos aproximados dos dados. O MySQL usa quatro bytes para valores de precisão única e oito bytes para valores de precisão dupla.

Para FLOAT, o padrão SQL permite uma especificação opcional da precisão (mas não do intervalo do expoente) em bits, após a palavra-chave FLOAT entre parênteses. O MySQL também suporta esta especificação de precisão opcional, mas o valor de precisão é usado apenas para determinar o tamanho do armazenamento. Uma precisão de 0 a 23 resulta em uma coluna FLOAT de precisão única de 4 bytes. Uma precisão de 24 a 53 resulta em uma coluna DOUBLE de precisão dupla de 8 bytes.

O MySQL permite uma sintaxe fora do padrão: FLOAT (M, D) ou REAL (M, D) ou DOUBLE PRECISION (M, D). Aqui, “(M, D)” significa que os valores podem ser armazenados com até M dígitos no total, dos quais os dígitos D podem estar após o ponto decimal. Por exemplo, uma coluna definida como FLOAT (7,4) será semelhante a -999,9999 quando exibida. O MySQL executa arredondamentos ao armazenar valores, portanto, se você inserir 999.00009 em uma coluna FLOAT (7,4), o resultado aproximado será 999.0001.

Como os valores de ponto flutuante são aproximados e não são armazenados como valores exatos, as tentativas de tratá-los como exatos nas comparações podem levar a problemas. Eles também estão sujeitos a dependências de plataforma ou implementação.

Para máxima portabilidade, o código que requer armazenamento de valores aproximados de dados numéricos deve usar FLOAT ou DOUBLE PRECISION sem especificação de precisão ou número de dígitos.

https://dev.mysql.com/doc/refman/5.5/en/floating-point-types.html

Problemas com valores de ponto flutuante

Os números de ponto flutuante às vezes causam confusão porque são aproximados e não são armazenados como valores exatos . Um valor de ponto flutuante, conforme escrito em uma instrução SQL, pode não ser o mesmo que o valor representado internamente. Tentativas de tratar valores de ponto flutuante como exatas nas comparações podem levar a problemas. Eles também estão sujeitos a dependências de plataforma ou implementação. Os tipos de dados FLOAT e DOUBLE estão sujeitos a esses problemas. Para colunas DECIMAIS, o MySQL executa operações com uma precisão de 65 dígitos decimais, o que deve resolver os problemas de imprecisão mais comuns.

O exemplo a seguir usa DOUBLE para demonstrar como os cálculos feitos usando operações de ponto flutuante estão sujeitos a erro de ponto flutuante.

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

O resultado está correto. Embora os cinco primeiros registros pareçam não satisfazer a comparação (os valores de aeb não parecem diferentes), eles podem fazê-lo porque a diferença entre os números aparece em torno da décima casa decimal, dependendo dos fatores como a arquitetura do computador ou a versão do compilador ou o nível de otimização. Por exemplo, CPUs diferentes podem avaliar números de ponto flutuante de maneira diferente.

Se as colunas d1 e d2 tivessem sido definidas como DECIMAL em vez de DOUBLE, o resultado da consulta SELECT teria apenas uma linha - a última mostrada acima.

A maneira correta de fazer a comparação de números de ponto flutuante é primeiro decidir sobre uma tolerância aceitável para diferenças entre os números e depois fazer a comparação com o valor de tolerância. Por exemplo, se concordarmos que os números de ponto flutuante devem ser considerados iguais se forem iguais dentro de uma precisão de um em dez mil (0,0001), a comparação deve ser escrita para encontrar diferenças maiores que o valor de tolerância:

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 outro lado, para obter linhas onde os números são iguais, o teste deve encontrar diferenças dentro do valor de tolerância:

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)

Os valores de ponto flutuante estão sujeitos a dependências de plataforma ou implementação. Suponha que você execute as seguintes instruções:

CREATE TABLE t1(c1 FLOAT(53,0), c2 FLOAT(53,0));
INSERT INTO t1 VALUES('1e+52','-1e+52');
SELECT * FROM t1;

Em algumas plataformas, a instrução SELECT retorna inf e -inf. Em outros, retorna 0 e -0.

Uma implicação dos problemas anteriores é que, se você tentar criar um escravo de replicação despejando o conteúdo da tabela com o mysqldump no mestre e recarregando o arquivo de despejo no escravo, as tabelas que contêm colunas de ponto flutuante podem diferir entre os dois hosts.

https://dev.mysql.com/doc/refman/5.5/en/problems-with-float.html

Pense grande
fonte
0

Regra rígida e rápida

Se tudo o que você precisa fazer é adicionar, subtrair ou multiplicar os números que você está armazenando, DECIMAL é o melhor.

Se você precisar dividir ou fazer qualquer outra forma de aritmética ou álgebra nos dados, você certamente ficará mais feliz com o float. As bibliotecas de ponto flutuante e nos processadores Intel, o próprio processador de ponto flutuante, têm TONs de operações para corrigir, corrigir, detectar e lidar com a nevasca das exceções que ocorrem ao executar funções matemáticas típicas - especialmente funções transcendentais.

Quanto à precisão, uma vez escrevi um sistema de orçamento que calculava a porcentagem de contribuição de cada uma das mais de 3.000 contas, para 3.600 unidades de orçamento, por mês, no nó de consolidação dessa unidade, e depois com base nessa matriz de porcentagens (3.000 + x 12 x 3.600) Multipliquei os valores orçados pelos nós organizacionais mais altos até os próximos três níveis dos nós organizacionais e depois calculei todos os valores (3.000 + 12) para todas as 3.200 unidades de detalhe. Milhões e milhões e milhões de cálculos de ponto flutuante de precisão dupla, qualquer um dos quais descartaria o acúmulo de todas essas projeções em uma consolidação de baixo para cima, voltando ao nível mais alto da organização.

O erro total de ponto flutuante após todos esses cálculos foi zero . Isso foi em 1986, e as bibliotecas de ponto flutuante hoje são muito, muito melhores do que eram antes. A Intel faz todos os seus cálculos intermediários de dobras na precisão de 80 bits, o que praticamente elimina o erro de arredondamento. Quando alguém lhe diz "é erro de ponto flutuante", é quase certo que NÃO é verdade.


fonte
-2

float(e double) representa frações binárias

decimal representa frações decimais

ReignBough
fonte
-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

em float quando o valor é definido como número inteiro, impressão 10, mas no decimal 11

user966380
fonte