Problema de ciclo e truncamento do servidor SQL
declare @amount float = 178.69999999
select
@amount as [amount],
round(@amount, 6) as [round],
round(round(@amount, 6), 2, 1) as [trim_1],
floor(round(@amount, 6) * power(10.0, 2)) / power(10.0, 2) as [trim_2]
E aqui está o resultado que recebo:
+--------------+-------+--------+--------+
| amount | round | trim_1 | trim_2 |
+--------------+-------+--------+--------+
| 178.69999999 | 178.7 | 178.69 | 178.7 |
+--------------+-------+--------+--------+
a ideia geral aqui é que estou a tentar arredondar por 6 casas decimais, depois aparar / andar / truncado por 2 casas decimais. Isso significa que estou esperando um resultado de 178.7
, mas estou obtendo um resultado de 178.69
para trim_1
(trim_2
é uma abordagem alternativa destinada a produzir o mesmo resultado).
round
, como a documentação do servidor SQL indica:
Sintaxe
ROUND ( numeric_expression , length [ ,function ] )
função
é o tipo de operação a realizar. function deve ser tinyint, smallint, ou int. Quando a função é omitida ou tem um valor de 0 (por omissão), o numeric_ expressão é arredondado. Quando for especificado um valor diferente de 0, numeric_ expression está truncado.
Então eu esperaria {[6] } para igualar trim_2
.
Aqui está o kicker: se eu passar o resultado de round
como uma constante, e não como uma variável, ele funciona como esperado:
select round(178.7, 2, 1) -- Yields 178.7
O meu palpite é que o servidor SQL está a fazer algo estranho com pontos flutuantes, ou de alguma forma consegui perder alguma coisa. Para que conste, estou a usar o servidor SQL 2014, por isso talvez seja esse o meu problema.
Eu gostaria de obter o resultado de ... o mínimo possível de código, para que o meu resultado final pareça mais limpo.
1 answers
Utilizar decimal
em vez de float
.
Retirado de flutuante e Real (Transact-SQL)
Os dados de vírgula flutuante são aproximados; portanto, nem todos os valores no intervalo do tipo de dados podem ser representados exactamente.
Substituir float
por decimal
no seu código tem o resultado desejado:
declare @amount decimal(18, 10) = 178.69999999
select
@amount as [amount],
round(@amount, 6) as [round],
round(round(@amount, 6), 2, 1) as [trim_1],
floor(round(@amount, 6) * power(10.0, 2)) / power(10.0, 2) as [trim_2]
Resultados:
╔════════════════╦════════════════╦════════════════╦════════════╗ ║ amount ║ round ║ trim_1 ║ trim_2 ║ ╠════════════════╬════════════════╬════════════════╬════════════╣ ║ 178.6999999900 ║ 178.7000000000 ║ 178.7000000000 ║ 178.700000 ║ ╚════════════════╩════════════════╩════════════════╩════════════╝