ROUND redondea un numero al entero mas cercano. Suena trivial, pero la funcion esconde dos estrategias distintas para tratar las mitades y una diferencia traicionera entre el tipo exacto numeric y el aproximado double precision, justo la que rompe informes financieros sin avisar.
Redondeo basico a entero
Con un solo argumento, ROUND descarta la parte decimal y salta al entero mas cercano. Las mitades (.5) van alejandose del cero: 2.5 pasa a 3 y -2.5 pasa a -3.
SELECT
ROUND(3.14159) AS a,
ROUND(2.5) AS b,
ROUND(3.5) AS c,
ROUND(-2.5) AS d;
Propiedades clave:
- Para
numeric el resultado conserva el tipo, la parte decimal simplemente se pone a cero.
- La estrategia para literales como
2.5 es half-away-from-zero, simetrica respecto al cero.
- Para conservar decimales existe un segundo argumento
ROUND(x, n), tratado en otro articulo.
numeric frente a double: donde se esconde el redondeo bancario
El gran tropiezo es que PostgreSQL redondea las mitades de forma distinta segun el tipo. El literal 2.5 es numeric y se aleja del cero. Pero double precision usa redondeo bancario (round-half-to-even): las mitades saltan al entero par mas cercano.
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;
Con double, tanto 2.5 como 3.5 redondean a 2 y 4 porque gana el vecino par bajo half-to-even. No es un bug, es una forma de cancelar el sesgo sistematico que aparece al sumar muchos valores redondeados.
Gotcha: el mismo numero da un resultado distinto solo por su tipo. Si tu consulta mezcla constantes y columnas float, los totales pueden bailar un centimo. Convierte a numeric de forma explicita cuando importe la previsibilidad.
Redondear dinero
Para el dinero, double es peligroso por un segundo motivo: no toda fraccion decimal es representable en coma flotante binaria. Compara un total acumulado:
SELECT
SUM(amount) AS raw_total,
ROUND(SUM(amount)) AS rounded_total
FROM orders
WHERE status = 'paid';
Si amount se declara numeric(12,2), todo es exacto. Si es double precision, puedes arrastrar colas como 19.999999998. La regla es simple: guarda el dinero como numeric y aplica ROUND sobre numeric.
Redondear el salario medio por departamento a entero:
SELECT
dept,
ROUND(AVG(salary)) AS avg_salary
FROM employees
GROUP BY dept
ORDER BY avg_salary DESC;
AVG sobre una columna numeric devuelve numeric, asi que el redondeo usa aqui de forma previsible half-away-from-zero.
Diferencias en MySQL y ClickHouse
ROUND existe en todas partes, pero la estrategia de las mitades cambia.
- MySQL:
ROUND(x) para tipos exactos (DECIMAL) redondea alejandose del cero, mientras que para tipos aproximados (DOUBLE) el comportamiento depende de la biblioteca C subyacente, normalmente redondeo bancario. La misma division que en PostgreSQL.
SELECT ROUND(2.5), ROUND(2.5e0);
- ClickHouse:
round() usa por defecto redondeo bancario, al entero par mas cercano. Si quieres el comportamiento habitual de alejarse del cero, usa roundBankers() para dejarlo claro o funciones dedicadas como floor/ceil.
SELECT round(2.5), round(3.5), roundBankers(2.5);
Recuerda una cosa: "redondear a entero" no es una sola operacion sino una familia de estrategias. Antes de confiar a ROUND un informe financiero, comprueba el tipo del argumento y como trata tu base de datos las mitades exactas.
ROUNDredondea un numero al entero mas cercano. Suena trivial, pero la funcion esconde dos estrategias distintas para tratar las mitades y una diferencia traicionera entre el tipo exactonumericy el aproximadodouble precision, justo la que rompe informes financieros sin avisar.Redondeo basico a entero
Con un solo argumento,
ROUNDdescarta la parte decimal y salta al entero mas cercano. Las mitades (.5) van alejandose del cero:2.5pasa a3y-2.5pasa a-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; -- -3Propiedades clave:
numericel resultado conserva el tipo, la parte decimal simplemente se pone a cero.2.5es half-away-from-zero, simetrica respecto al cero.ROUND(x, n), tratado en otro articulo.numeric frente a double: donde se esconde el redondeo bancario
El gran tropiezo es que PostgreSQL redondea las mitades de forma distinta segun el tipo. El literal
2.5esnumericy se aleja del cero. Perodouble precisionusa redondeo bancario (round-half-to-even): las mitades saltan al entero par mas cercano.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; -- 4Con
double, tanto2.5como3.5redondean a2y4porque gana el vecino par bajo half-to-even. No es un bug, es una forma de cancelar el sesgo sistematico que aparece al sumar muchos valores redondeados.Gotcha: el mismo numero da un resultado distinto solo por su tipo. Si tu consulta mezcla constantes y columnas
float, los totales pueden bailar un centimo. Convierte anumericde forma explicita cuando importe la previsibilidad.Redondear dinero
Para el dinero,
doublees peligroso por un segundo motivo: no toda fraccion decimal es representable en coma flotante binaria. Compara un total acumulado:SELECT SUM(amount) AS raw_total, ROUND(SUM(amount)) AS rounded_total FROM orders WHERE status = 'paid';Si
amountse declaranumeric(12,2), todo es exacto. Si esdouble precision, puedes arrastrar colas como19.999999998. La regla es simple: guarda el dinero comonumericy aplicaROUNDsobrenumeric.Redondear el salario medio por departamento a entero:
SELECT dept, ROUND(AVG(salary)) AS avg_salary FROM employees GROUP BY dept ORDER BY avg_salary DESC;AVGsobre una columnanumericdevuelvenumeric, asi que el redondeo usa aqui de forma previsible half-away-from-zero.Diferencias en MySQL y ClickHouse
ROUNDexiste en todas partes, pero la estrategia de las mitades cambia.ROUND(x)para tipos exactos (DECIMAL) redondea alejandose del cero, mientras que para tipos aproximados (DOUBLE) el comportamiento depende de la biblioteca C subyacente, normalmente redondeo bancario. La misma division que en PostgreSQL.SELECT ROUND(2.5), ROUND(2.5e0); -- 3 (DECIMAL, away from zero), often 2 (DOUBLE, to even)round()usa por defecto redondeo bancario, al entero par mas cercano. Si quieres el comportamiento habitual de alejarse del cero, usaroundBankers()para dejarlo claro o funciones dedicadas comofloor/ceil.SELECT round(2.5), round(3.5), roundBankers(2.5); -- 2, 4, 2Recuerda una cosa: "redondear a entero" no es una sola operacion sino una familia de estrategias. Antes de confiar a
ROUNDun informe financiero, comprueba el tipo del argumento y como trata tu base de datos las mitades exactas.