Pergunta

Eu fiz alguns testes com cálculos de ponto flutuante para minimizar a perda de precisão. Eu tropecei através de um phenomen eu quero mostrar aqui e espero obter uma explicação.

Quando eu escrevo

print 1.0 / (1.0 / 60.0)

O resultado é

60.0024000960

Quando eu escrever a mesma fórmula e fazer conversão explícita para float

print cast(1.0 as float) / (cast(1.0 as float) / cast(60.0 as float))

O resultado é

60

Até agora eu pensava que os literais numéricos com casas decimais são automaticamente tratadas como valores float com a precisão adequada. A projeção para espectáculos real o mesmo resultado que a projeção para float.

  • Existe alguma documentação sobre como SQL Server avalia literais numéricos?
  • De que tipo de dados são aquelas literais?
  • Eu realmente tenho para lançá-los para float obter melhor precisão (que soa como ironia para mim:)?
  • Existe uma maneira mais fácil do que desordenar minhas fórmulas com elencos?
Foi útil?

Solução

SQL Server usa o menor tipo de dados possível.

Quando você executar este script

SELECT SQL_VARIANT_PROPERTY(1.0, 'BaseType')
SELECT SQL_VARIANT_PROPERTY(1.0, 'Precision')
SELECT SQL_VARIANT_PROPERTY(1.0, 'Scale')
SELECT SQL_VARIANT_PROPERTY(1.0, 'TotalBytes')

Você verá que SQL Server usado implicitamente uma numérico (2, 1) tipo de dados.
A divisão por 60,0 convertidos o resultado numérico (8, 6).
O cálculo final converte o resultado para numérico (17, 10).


Editar

Tomado de SQL Server Books Online Tipo de conversão de dados

declarações No Transact-SQL, uma constante com um ponto decimal é automaticamente convertido para um valor de dados numéricos, usando a precisão mínimo e escala necessário. Por exemplo, a constante 12.345 é convertido num valor numérico com uma precisão de 5 e um escala de 3.

Outras dicas

Sim, você frequentemente tem que lançá-los flutuar obter melhor precisão. Minha opinião sobre ele:

Para melhores casas decimais de precisão elenco antes cálculos

Eu acho que deve ser entendido o que está acontecendo nos bastidores para referência futura em casos semelhantes.

Os valores numéricos literais com ponto decimal excluindo notação científica representam tipo de dados Decimal que é armazenado como menor tipo Decimal possível. Mesma citação como Lieven Keersmaekers de partir: https://msdn.microsoft.com/en -us / library / ms191530% 28SQL.90% 29.aspx # _decimal

declarações No Transact-SQL, uma constante com um ponto decimal é automaticamente convertidas em um valor de dados numéricos, usando o mínimo precisão e escala necessária. Por exemplo, a constante de 12.345 está convertido em um valor numérico com uma precisão de 5 e uma escala de 3.

Os zeros à direita à direita do ponto decimal especificar escala. Os zeros à esquerda à esquerda do ponto decimal são ignorados.

Alguns exemplos:

1.0  -> Decimal(2,1)
60.0 -> Decimal(3,1)
1.00 -> Decimal(3,2)
01.0 -> Decimal (2,1)

Outro ponto a considerar é Tipo de dados precedência . Quando um operador combina duas expressões de diferentes tipos de dados, as regras para o tipo de dados precedência especificar que o tipo de dados com a precedência mais baixa é convertido para o tipo de dados com a maior precedência. E ainda outro ponto a considerar é se fizermos operações aritméticas sobre os tipos de decimais que o tipo Decimal resultante, ou seja, precisão e escala dependem de ambos os operadores e da própria operação. Isto é descrito no documento precisão, escala e comprimento .

Assim, parte de sua expressão entre parênteses

( 1.0 / 60.0 ) is evaluated to 0.016666 and the resulting type is Decimal (8,6)

usando acima regras sobre Precisão e escala de expressões decimais. Além disso, o banqueiro de arredondamento ou arredondamento até mesmo é utilizado. É importante notar arredondamento diferente para Decimal e tipo flutuante são usados. Se continuarmos a expressão

1.0 / 0.016666 is evaluated to 60.002400096 and the resulting type is Decimal (17,10)

Assim, a parte da discrepância é devido ao arredondamento diferente sendo usado para tipos de decimais do que para float.

De acordo com as regras acima seria suficiente para usar apenas um elenco dentro de parênteses. Cada outro literal será promovido a flutuar de acordo com regras tipo de dados precedência.

1.0 / (1.0 / cast(60.0 as float))

E Uma coisa mais importante. Mesmo esta expressão flutuador não calcula resultado exato. É apenas para que o front-end (SSMS ou qualquer outro) arredonda o valor para (eu acho) de precisão 6 dígitos e depois trunca zeros à direita. Então isto é 1.000001 torna-se 1.

Simples, não é?

Para escrever uma expressão flutuador constante, tentar usar a notação científica:

select (1.0E0 / (1.0E0 / 60.0E0))

O resultado é 60.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top