Database
 sql >> Base de Dados >  >> RDS >> Database

SQL FLOAT:3 pontos que ajudarão você a evitar erros matemáticos estranhos


Você já pensou que o SQL pode estar errado em matemática? Parece loucura. Mas se você usou o tipo de dados SQL FLOAT, pode ter se deparado com os problemas que estou prestes a mostrar.

Considere isto. 0,1 + 0,2 deve ser 0,3, certo? Mas verifique isso usando o tipo de dados SQL FLOAT.
DECLARE @f1 FLOAT = 0.1
DECLARE @f2 FLOAT = 0.2

SELECT CASE WHEN @f1 + @f2 = .3 THEN 1 ELSE 0 END

O resultado correto é 1. Mas verifique a Figura 1.

Eu tenho sua atenção agora? Eu certamente espero que sim. É bastante assustador depender de um sistema que não nos dá a matemática correta. Mas este artigo irá ajudá-lo a evitar isso.

Há algum trabalho a fazer. Precisamos começar do que é um tipo de dados FLOAT.

O que é o tipo de dados SQL FLOAT?


O tipo de dados SQL FLOAT é um tipo de dados numérico aproximado usado para números de ponto flutuante. Eles podem armazenar números muito grandes ou muito pequenos. Eles também são usados ​​para cálculos que exigem tempos de processamento rápidos.

Tudo isso tem o custo da perda de precisão. Além disso, você não pode dizer onde o ponto decimal será colocado após o cálculo - ele flutua . Enquanto isso, números exatos como DECIMAL terão uma posição de ponto decimal fixo.

Como você declara um tipo de dados SQL FLOAT


A sintaxe é FLOAT[(n)], onde n é o número de bits usados ​​para armazenar a mantissa de um número de ponto flutuante em notação científica. Isso também determina a precisão e o tamanho do armazenamento. Os valores possíveis para n estão entre 1 e 53. Observe que n é opcional.

Aqui está um exemplo:
DECLARE @floatValue1 FLOAT;   -- Float variable without the number of bits
DECLARE @floatValue2 FLOAT(3) -- Float variable with 3 bits 

Se você não especificar n , o padrão é 53. Esse também é o valor máximo. Além disso, FLOAT(53) é um número de ponto flutuante de precisão dupla ou binário64. Além de usar FLOAT(53), você também pode declará-lo como DOUBLE PRECISION.

As 3 declarações a seguir são funcionalmente equivalentes:
DECLARE @double1 FLOAT(53); 
DECLARE @double2 FLOAT;
DECLARE @double3 DOUBLE PRECISION;

A tabela mostra o número de bits e o tamanho de armazenamento correspondente.
Valor de n Tamanho do armazenamento
1 a 24 4 bytes
25 a 53 8 bytes

O SQL FLOAT e o REAL são a mesma coisa?


REAL também é FLOAT(24). Também é referido como precisão simples ou binário32.

Por que saber isso é importante


Saber que este é um número aproximado impedirá que você o use para cálculos que exigem precisão. Você também está preocupado com armazenamento e memória? Use REAL ou FLOAT(24) se não precisar de valores muito grandes ou muito pequenos.

Quais são as diferenças entre FLOAT e DECIMAL?


FLOAT é um número aproximado. DECIMAL é um número exato. Aqui está um resumo das diferenças em uma tabela:
FLUTUAR DECIMAL
Ponto Decimal Pode ser colocado em qualquer lugar do dígito Posição fixa
Limite máximo 38 dígitos ou 99.999.999.999.999.999.999.999.999.999.999.999.999 FLOAT(53) tem um alcance máximo de 1,79E+308 ou 179 seguido por 306 zeros
Armazenamento Máximo de 8 bytes Máximo de 17 bytes
Resultado computacional Aproximado Exato
Verificações de comparação Não use =ou <>. Evite ao arredondar =ou <> operadores. Bom para arredondar

Você já viu na Figura 1 como calcular um número FLOAT pode ter resultados estranhos. Se você alterar o tipo de dados para DECIMAL assim:
DECLARE @d1 DECIMAL(2,1) = 0.1
DECLARE @d2 DECIMAL(2,1) = 0.2

SELECT CASE WHEN @d1 + @d2 = 0.3 THEN 1 ELSE 0 END 

O resultado estará correto.
Usar um operador de desigualdade também é um problema. Confira o circuito abaixo.
DECLARE @floatValue FLOAT(1) = 0.0

WHILE @floatValue <> 5.0
BEGIN
	PRINT @floatValue;
	SET @floatValue += 0.1;
END 

O que você acha? Veja a Figura 2 abaixo.

Estrondo! Loop infinito! A condição de desigualdade será sempre verdadeira. Portanto, a escolha lógica é alterar o tipo para DECIMAL.
DECLARE @decimalValue DECIMAL(2,1) = 0.0

WHILE @decimalValue <> 5.0
BEGIN
	PRINT @decimalValue;
	SET @decimalValue += 0.1;
END 

O código acima certamente irá parar quando @decimalValue é igual a 5,0. Veja você mesmo na Figura 3 abaixo.

Legal! Mas se você ainda insistir em FLOAT, isso funcionará bem sem o loop infinito.
DECLARE @floatValue FLOAT(1) = 0.0

WHILE @floatValue < 5.0
BEGIN
	PRINT @floatValue;
	SET @floatValue += 0.1;
END

Enquanto isso, o arredondamento também está desativado. Considere o seguinte:
DECLARE @value FLOAT(2) = 1.15

SELECT ROUND(@value, 1)  -- This will result to 1.1

Em vez de 1.20, o código resulta em 1.1. Mas se você usar DECIMAL, o resultado será correto.
DECLARE @value DECIMAL(3,2) = 1.15

SELECT ROUND(@value, 1)  -- This will result in 1.2 or 1.20

Quando FLOAT está correto e DECIMAL não


Os números exatos NÃO são tão exatos o tempo todo? Para reproduzir esse problema, usaremos um cálculo e, em seguida, o reverteremos. Primeiro, vamos preparar os dados.
CREATE TABLE ExactNumerics1
(
	fixed1 DECIMAL(8,4),
	fixed2 DECIMAL(8,4),
	fixed3 DECIMAL(8,4),
	calcValue1 AS fixed3 / fixed1 * fixed2
)
GO

INSERT INTO ExactNumerics1
(fixed1,fixed2,fixed3)
VALUES
(54,0.03,1*54/0.03)

A tabela acima usará valores fixos para as 2 primeiras colunas. A terceira coluna terá o cálculo. Por fim, a quarta, que é uma coluna computada, fará o cálculo inverso. O resultado correto na coluna calculada deve ser 1.

Agora, para compará-lo ao FLOAT, vamos criar uma tabela e dados semelhantes.
CREATE TABLE ApproxNumerics1
(
	float1 FLOAT(2),
	float2 FLOAT(2),
	float3 FLOAT(2),
	calcValue1 AS float3 / float1 * float2 
)

INSERT INTO ApproxNumerics1
(float1, float2, float3)
VALUES
(54,0.03,1*54/0.03)

Vamos consultar.
SELECT * FROM ApproxNumerics1
SELECT * FROM ExactNumerics1

Os resultados? Confira a Figura 4.

O que aconteceu aqui? FLOAT acertou, mas DECIMAL não. Algo deu errado.

CONVERSÃO IMPLÍCITA FAZ ISSO NOVAMENTE


A conversão implícita acontece porque o SQL perdoa. Quando diferentes tipos de dados são usados ​​em um cálculo, o SQL Server tenta convertê-lo usando a conversão implícita nas nossas costas.

Uma conversão realmente aconteceu? Além disso, cada coluna no ExactNumerics1 tabela é um DECIMAL.

Vamos verificar a estrutura da tabela do ExactNumerics1 tabela no SQL Server Management Studio:

Observe a área de caixa vermelha na Figura 3. A coluna computada é um DECIMAL(30,17), não um DECIMAL(8,4). De acordo com a documentação oficial, 2 colunas DECIMAL com precisão e escala diferentes são 2 tipos de dados diferentes . Veja você mesmo aqui. Por causa da diferença, é necessária uma conversão. Assim, a conversão implícita entra em jogo.

E se eles são diferentes e uma conversão implícita aconteceu?

Novamente, com base na documentação oficial, uma perda de precisão ou escala pode ocorrer durante a conversão implícita . Assim, é necessário um CAST explícito. Observe o tipo de dados DECIMAL na tabela de conversão nessa referência.

Alguma perda aconteceu aqui. Se a coluna calculada também for DECIMAL(8,4), a conversão implícita não ocorre.

Para evitar a conversão implícita, siga a documentação oficial. A estrutura da tabela deveria ter ficado assim:
CREATE TABLE ExactNumerics2
(
	fixed1 DECIMAL(8,4),
	fixed2 DECIMAL(8,4),
	fixed3 DECIMAL(8,4),
	calcValue1 AS CAST(fixed3 / fixed1 * fixed2 AS DECIMAL(8,4)) -- the explicit CAST
)

O CAST explícito na coluna calculada garante que os tipos de dados sejam consistentes. Se também seguirmos essa estrutura e inserirmos os mesmos dados, o resultado estará correto. Confira a nova saída na Figura 6 abaixo.

Eventualmente, os números exatos não serão exatos se ocorrer uma conversão implícita entre 2 ou mais valores DECIMAL.

Por que saber isso é importante


Dá uma ideia do que você precisa para suas tabelas e variáveis. Além disso, a conversão implícita pode fazer com que até mesmo números exatos fiquem malucos. Portanto, defina explicitamente a precisão e a escala e seja consistente com ela em seus cálculos.

Devo usar SQL FLOAT para dados financeiros?


Ao calcular porcentagens em cada fatia de um gráfico de pizza, a soma deve ser 100%. Os totais nos relatórios resumidos e detalhados também devem ser consistentes. Se a precisão dos resultados for crucial, um tipo de dados aproximado como FLOAT não fará o trabalho. A escolha lógica para isso é DECIMAL.

Mas fica uma pergunta.

Quando você deve usar FLOAT?


Use FLOAT para dados que exigem valores astronômicos, como distâncias entre galáxias. Enquanto isso, o tipo de dado DECIMAL sofrerá um estouro aritmético com esse tipo de dado. Valores minúsculos como o diâmetro de um núcleo atômico também se encaixam usando FLOAT. Dados científicos e outros valores que não exigem precisão também podem se beneficiar do FLOAT.

Por que saber isso é importante


Não dizemos que FLOAT é ruim e DECIMAL é bom ou vice-versa. Conhecer os casos de uso corretos para cada um fornecerá a você e a seus usuários os resultados pretendidos. E, novamente, você quer seus usuários felizes, certo?

Conclusão


No final do dia, todos nós queremos fazer nossos trabalhos e ser bons neles. A matemática sempre fará parte do nosso trabalho. E conhecer os tipos de dados numéricos corretos também nos ajudará a lidar com isso. Não é difícil se você sabe o que está fazendo.

Espero que este artigo tenha ajudado a evitar matemática estranha no SQL Server.

Você tem mais alguma coisa a acrescentar? Então, deixe-nos saber na seção de comentários. Compartilhe isso também em suas plataformas de mídia social favoritas.