Articles

os perigos de usar tipos de dados reais ou flutuantes

existe uma velha piada sobre aritmética de ponto flutuante:

“Se eu cortar um bolo em três, cada pedaço é 0,33 do bolo. Se voltar a juntar as três peças, dou-me 0,99 do bolo. Para onde foi o resto do bolo?”
— ” Simple. Esse é o pequeno pedaço preso na faca ”

aritmética de ponto flutuante é tudo sobre a tolerância e gerenciamento de aproximação, a fim de evitar erros de transbordamento em cálculos. No mundo real, Nós geralmente nos preocupamos com a precisão em números e vamos, em vez disso, sacrificar o espaço e os recursos, a fim de evitar o excesso de fluxo.enquanto a ciência trabalha alegremente dentro de uma margem de erro, a precisão é importante na contabilidade empresarial. Quando eu era um programador de filhotes, uma vez escrevi o que eu pensei ser uma forma perfeitamente adequada de calcular o lucro de negócios de corretores. Em um milhão de libras, era um penny ou dois fora no máximo. Fiquei muito satisfeito. Ele usou os cálculos inerentes ao compilador PL/1 que usamos na época para desenvolver pacotes financeiros. Mostrei-lhes a aplicação finamente elaborada, e ficaram horrorizados. Um centavo em um milhão de libras parecia para os comerciantes da cidade hardboiled ser imprudente. Eles não aceitariam. Eu fui forçado a escrever um pacote binário-codificado-decimal (BCD) em código de montagem que era precisamente preciso.

SQL Prompt tem uma regra de análise de código (BP023) que irá alertá-lo para o uso de datatypesFLOAT ou REAL, devido às imprecisões significativas que eles podem introduzir para o tipo de cálculos que muitas organizações irão executar rotineiramente em seus dados do servidor SQL.

tipos de dados de número aproximado

aritmética de ponto flutuante foi concebida numa altura em que era uma prioridade salvar a memória enquanto dava uma maneira versátil de fazer cálculos que envolvia grandes números. Embora ainda seja útil para muitos tipos de cálculos científicos, particularmente aqueles que se conformam com o padrão de precisão dupla IEEE 754 para aritmética de ponto flutuante, é, necessariamente, um compromisso. A pista está no nome deste tipo de dados e aritmética: ‘aproximado’. Os números de vírgula flutuante não podem representar com precisão todos os números reais: adicionalmente, as operações de vírgula flutuante não podem representar com precisão todas as operações aritméticas. No entanto, o intervalo de magnitude do número que eles podem manter é muito maior do que é possível em outros tipos numéricos, mesmo que nem sempre é mantido com precisão.

Os problemas que surgem do uso de cálculos de vírgula flutuante são devidos ao arredondamento durante cálculos complexos, e são mais frequentemente vistos se os dados são “mal condicionados”, de modo que pequenas mudanças na entrada são ampliadas na saída. As imprecisões são muito menos aparentes com maior precisão da representação dos números, mas eles ainda estão presentes, no entanto. Existem também algumas restrições Esotéricas no uso de números que são válidos mas não podem ser representados em ponto flutuante, como tan(π/2), mas estes são susceptíveis de excitar apenas matemáticos.

SQL Server de dados de ponto flutuante

O SQL Padrão tem três em ponto flutuante, o número aproximado de tipos de dados, REALDOUBLEPRECISION e FLOAT(n). O servidor SQL está em conformidade com isso, exceto que não tem DOUBLEPRECISION datatype, usando FLOAT(53) em vez disso. FLOAT(24) e FLOAT(53) tipos de dados corresponde a Binary32 (Único) e Binary64 (duplo) no padrão IEEE 754, e são armazenados em 4 e 8 bytes, e 7 e 16 dígitos realizada, de acordo. Eles são úteis quando é importante que os cálculos produzam o mesmo resultado que uma aplicação usando o framework.NET que também usa o IEEE 754. O tipo de precisão dupla também é necessário quando os números excedem em sua magnitude o máximo permitido pelo DECIMAL datatype (38 dígitos) embora com perda de precisão. Números aproximados não podem, É claro, ser usados de forma confiável em qualquer teste de igualdade, como uma cláusula WHERE.

cálculos usando o tipo de dados REAL (precisão única)

eu vou experimentar o REAL datatype. TheFLOAT(24) datatype, or smaller, reacts the same way. A primeira coisa a lembrar ao experimentar com números de vírgula flutuante no servidor SQL é que o SSMS torna um número de vírgula flutuante de uma forma que disfarça pequenas diferenças. Por exemplo:

1
SELECT Convert(REAL,0.100000001490116119384765625)

…dá 0.1

Para ver com mais precisão o valor que é armazenado em um número de ponto flutuante, você tem que usar a função STR (), especificando a precisão que você realmente deseja.

1
2
3
4
5

/*estou sendo um pouco injusto aqui, porque o número decimal 0.1 não é representável
em ponto flutuante; a representação binária exata teria um “1100” sequência
continuar indefinidamente:*/
DECLARE @FirstApproximate REAL = 0.1
SELECIONE Str(@FirstApproximate,20,16) –deve ser 0.100000001490116119384765625

Já, isso é alarmante. Afinal de contas, estamos a lidar com dados com milhões de filas de tal modo que pequenos erros se acumularão a menos que, como os “banqueiros a arredondar”, se averigúem. Esse erro já está próximo do “penny in a million pounds” (1 / 240000000) que mencionei na introdução!vamos evitar 0,1 e colocá-lo numa aberração de ponto flutuante. Que tal dividir 1 por 3. De certeza que isto não pode ser um problema?

1
2
3
4
5

INFORMANDO @pagamentos de salários REAIS = 1
INFORMANDO @divisor REAL =3
SELECIONE Str(@pagamentos de salários /@divisor,20,16) como quociente
–produz 0.3333333432674408
–deve ser 0.3333333333333333

Oops. Percebeu mal. Está bem, é um pequeno erro, mas lembra-te da minha história sobre os banqueiros. Uma resposta é certa ou errada, não há tons de cinzento para os homens de fato cinzento. Na escola de gestão, há apenas uma carraça e uma cruz. Nenhum sinal que signifique “perto o suficiente”.

um teste simples é dividir um por números de um para vinte. O que pode correr mal?

Podemos armazenar os resultados do ponto flutuante e do cálculo numérico, ambos convertidos em cadeias de caracteres e depois comparamos as cadeias de caracteres (seja avisado que STR() pode colocar em um espaço de liderança que torna uma complicação).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

DECLARE @divisor REAL, @dividend REAL = 1
DECLARE @FloatingPointCalculations TABLE (Divisor INT, QuotientFloatingPoint VARCHAR(20), QuotientNumeric VARCHAR(20));
DECLARE @ii INT = 1;
DECLARE @iiMax INT = 20;
WHILE (@ii <= @iiMax)
BEGIN
SELECT @divisor = @ii;
INSERT INTO @FloatingPointCalculations (Divisor, QuotientFloatingPoint,
QuotientNumeric)
SELECT @ii AS divisor, Str(@Dividend / @divisor, 20, 16) AS QuotientFloatingPoint,
Convert(VARCHAR(20), 1.0000000 / @ii) AS QuotientNumeric;
SELECT @ii += 1;
END;
SELECT The.Divisor, The.QuotientFloatingPoint, The.QuotientNumeric
FROM @FloatingPointCalculations AS The;

Now, what if we list the rows where the numbers don’t match?

1
2
3

SELECT The.Divisor, The.QuotientFloatingPoint, The.QuotientNumeric
FROM @FloatingPointCalculations The
WHERE Left(LTrim(The.QuotientFloatingPoint),16)<> Left(LTrim(The.QuotientNumeric),16)

Ugh! Somente onde o divisor era 1, 2, 4, 8 ou 16 havia um resultado correto.

no caso de você estar esperando que de alguma forma o float era preciso, e a versão numérica não era, aqui está o quociente numérico calculado no Excel:

Cálculos usando FLOAT(25) ou longo (precisão dupla)

Se você usar o dobro de ponto flutuante de precisão, FLOAT(25) ou mais, os testes são todos passados, porque de STR() função permite um máximo de dezasseis casas à direita da vírgula decimal. Se houver mais de 16 Então o resultado é truncado. O datatype de precisão dupla tem dezesseis dígitos, enquanto o datatype de precisão única tem sete. Você também terá visto que o único tipo de dados de precisão recebe os primeiros sete dígitos para a direita. Da mesma forma, a precisão dupla obtém os primeiros dezesseis dígitos para a direita. Podemos alargar o número para ver a aproximação.

1
2

DECLARE @FirstApproximate FLOAT(53) = 10000000000000000.1
SELECIONE Str(@FirstApproximate,40,16) COMO BigNumberWithaDecimal

Que parte fracionária desapareceu, não é? É provavelmente apenas uma pequena diferença, mas em alguns cálculos, pode causar problemas.

conclusão

aritmética de ponto flutuante é rápida e econômica no armazenamento, mas fornece um resultado aproximado. É adequado para aplicações científicas bem condicionadas, mas não para cálculos financeiros, que exigem que um número seja “certo” ou “errado”. Ele também tem a desvantagem extra em uma base de dados, porque você não pode confiar e consistentemente testar dois números aproximados para a igualdade.

não é correcto dizer que nunca deve usar números de vírgula flutuante em tipos de dados SQL ou em aritmética. Os tipos aproximados estão lá no padrão SQL para uma finalidade. Eu iria hoje em dia sempre ficar com a dupla precisão de ponto flutuante datatype no servidor SQL, onde há um requisito adequado. Eles são grandes para fins como modelização de sistemas meteorológicos, ou traçando trajetórias, mas não para os tipos de cálculos para os quais a organização média é susceptível de usar um banco de dados.

Se detectar uma utilização errada destes tipos, deve mudar para um ID

/NUMERICtipo em vez disso. Se sabe que precisa de aritmética de vírgula flutuante e pode explicar porquê, então provavelmente sabe o suficiente para evitar as armadilhas de vírgula flutuante, como a que ocorreu na famosa falha de mísseis Patriot que levou directamente a 28 mortes.