ROUND arredonda um numero para o inteiro mais proximo. Parece trivial, mas a funcao esconde duas estrategias distintas para lidar com as metades e uma diferenca traicoeira entre o tipo exato numeric e o aproximado double precision, exatamente a que quebra relatorios financeiros sem aviso.
Arredondamento basico para inteiro
Com um unico argumento, ROUND descarta a parte decimal e salta para o inteiro mais proximo. As metades (.5) vao se afastando do zero: 2.5 vira 3 e -2.5 vira -3.
SELECT
ROUND(3.14159) AS a,
ROUND(2.5) AS b,
ROUND(3.5) AS c,
ROUND(-2.5) AS d;
Propriedades principais:
- Para
numeric o resultado mantem o tipo, a parte decimal simplesmente vai a zero.
- A estrategia para literais como
2.5 e half-away-from-zero, simetrica em relacao ao zero.
- Para manter casas decimais existe um segundo argumento
ROUND(x, n), tratado em outro artigo.
numeric vs double: onde o arredondamento bancario se esconde
A grande pegadinha e que o PostgreSQL arredonda as metades de forma diferente conforme o tipo. O literal 2.5 e numeric e se afasta do zero. Mas double precision usa arredondamento bancario (round-half-to-even): as metades saltam para o inteiro par mais proximo.
SELECT
ROUND(2.5::numeric) AS num_25,
ROUND(3.5::numeric) AS num_35,
ROUND(2.5::double precision) AS dbl_25,
ROUND(3.5::double precision) AS dbl_35;
Com double, tanto 2.5 quanto 3.5 arredondam para 2 e 4 porque o vizinho par vence sob half-to-even. Nao e um bug, e um jeito de cancelar o vies sistematico que surge ao somar muitos valores arredondados.
Pegadinha: o mesmo numero da um resultado diferente apenas por causa do tipo. Se a sua consulta mistura constantes e colunas float, os totais podem dancar um centavo. Faca cast para numeric de forma explicita quando a previsibilidade importar.
Arredondando dinheiro
Para dinheiro, double e perigoso por um segundo motivo: nem toda fracao decimal e representavel em ponto flutuante binario. Compare um total acumulado:
SELECT
SUM(amount) AS raw_total,
ROUND(SUM(amount)) AS rounded_total
FROM orders
WHERE status = 'paid';
Se amount for declarado numeric(12,2), tudo e exato. Se for double precision, voce ja pode carregar caudas como 19.999999998. A regra e simples: guarde dinheiro como numeric e aplique ROUND sobre numeric.
Arredondar o salario medio por departamento para inteiro:
SELECT
dept,
ROUND(AVG(salary)) AS avg_salary
FROM employees
GROUP BY dept
ORDER BY avg_salary DESC;
AVG sobre uma coluna numeric retorna numeric, entao o arredondamento aqui usa previsivelmente half-away-from-zero.
Diferencas no MySQL e ClickHouse
ROUND existe em todo lugar, mas a estrategia das metades muda.
- MySQL:
ROUND(x) para tipos exatos (DECIMAL) arredonda se afastando do zero, enquanto para tipos aproximados (DOUBLE) o comportamento depende da biblioteca C subjacente, normalmente arredondamento bancario. A mesma divisao do PostgreSQL.
SELECT ROUND(2.5), ROUND(2.5e0);
- ClickHouse:
round() usa por padrao arredondamento bancario, para o inteiro par mais proximo. Se voce quer o comportamento familiar de se afastar do zero, use roundBankers() para deixar claro ou funcoes dedicadas como floor/ceil.
SELECT round(2.5), round(3.5), roundBankers(2.5);
Lembre-se de uma coisa: "arredondar para inteiro" nao e uma operacao unica, mas uma familia de estrategias. Antes de confiar ao ROUND um relatorio financeiro, verifique o tipo do argumento e como o seu banco trata as metades exatas.
ROUNDarredonda um numero para o inteiro mais proximo. Parece trivial, mas a funcao esconde duas estrategias distintas para lidar com as metades e uma diferenca traicoeira entre o tipo exatonumerice o aproximadodouble precision, exatamente a que quebra relatorios financeiros sem aviso.Arredondamento basico para inteiro
Com um unico argumento,
ROUNDdescarta a parte decimal e salta para o inteiro mais proximo. As metades (.5) vao se afastando do zero:2.5vira3e-2.5vira-3.SELECT ROUND(3.14159) AS a, -- 3 ROUND(2.5) AS b, -- 3 ROUND(3.5) AS c, -- 4 ROUND(-2.5) AS d; -- -3Propriedades principais:
numerico resultado mantem o tipo, a parte decimal simplesmente vai a zero.2.5e half-away-from-zero, simetrica em relacao ao zero.ROUND(x, n), tratado em outro artigo.numeric vs double: onde o arredondamento bancario se esconde
A grande pegadinha e que o PostgreSQL arredonda as metades de forma diferente conforme o tipo. O literal
2.5enumerice se afasta do zero. Masdouble precisionusa arredondamento bancario (round-half-to-even): as metades saltam para o inteiro par mais proximo.SELECT ROUND(2.5::numeric) AS num_25, -- 3 ROUND(3.5::numeric) AS num_35, -- 4 ROUND(2.5::double precision) AS dbl_25, -- 2 ROUND(3.5::double precision) AS dbl_35; -- 4Com
double, tanto2.5quanto3.5arredondam para2e4porque o vizinho par vence sob half-to-even. Nao e um bug, e um jeito de cancelar o vies sistematico que surge ao somar muitos valores arredondados.Pegadinha: o mesmo numero da um resultado diferente apenas por causa do tipo. Se a sua consulta mistura constantes e colunas
float, os totais podem dancar um centavo. Faca cast paranumericde forma explicita quando a previsibilidade importar.Arredondando dinheiro
Para dinheiro,
doublee perigoso por um segundo motivo: nem toda fracao decimal e representavel em ponto flutuante binario. Compare um total acumulado:SELECT SUM(amount) AS raw_total, ROUND(SUM(amount)) AS rounded_total FROM orders WHERE status = 'paid';Se
amountfor declaradonumeric(12,2), tudo e exato. Se fordouble precision, voce ja pode carregar caudas como19.999999998. A regra e simples: guarde dinheiro comonumerice apliqueROUNDsobrenumeric.Arredondar o salario medio por departamento para inteiro:
SELECT dept, ROUND(AVG(salary)) AS avg_salary FROM employees GROUP BY dept ORDER BY avg_salary DESC;AVGsobre uma colunanumericretornanumeric, entao o arredondamento aqui usa previsivelmente half-away-from-zero.Diferencas no MySQL e ClickHouse
ROUNDexiste em todo lugar, mas a estrategia das metades muda.ROUND(x)para tipos exatos (DECIMAL) arredonda se afastando do zero, enquanto para tipos aproximados (DOUBLE) o comportamento depende da biblioteca C subjacente, normalmente arredondamento bancario. A mesma divisao do PostgreSQL.SELECT ROUND(2.5), ROUND(2.5e0); -- 3 (DECIMAL, away from zero), often 2 (DOUBLE, to even)round()usa por padrao arredondamento bancario, para o inteiro par mais proximo. Se voce quer o comportamento familiar de se afastar do zero, useroundBankers()para deixar claro ou funcoes dedicadas comofloor/ceil.SELECT round(2.5), round(3.5), roundBankers(2.5); -- 2, 4, 2Lembre-se de uma coisa: "arredondar para inteiro" nao e uma operacao unica, mas uma familia de estrategias. Antes de confiar ao
ROUNDum relatorio financeiro, verifique o tipo do argumento e como o seu banco trata as metades exatas.