sqlpostgresqlroundnumeric

ROUND en SQL: redondear al entero mas cercano, redondeo bancario y dinero

Como ROUND redondea al entero mas cercano, por que half-away-from-zero difiere del redondeo bancario y por que numeric supera a double para dinero.

2 min de lecturaReferencesql · postgresql · round · numeric · money · clickhouse

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,   -- 3
  ROUND(2.5)     AS b,   -- 3
  ROUND(3.5)     AS c,   -- 4
  ROUND(-2.5)    AS d;   -- -3

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,   -- 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;   -- 4

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);
-- 3 (DECIMAL, away from zero), often 2 (DOUBLE, to even)
  • 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);
-- 2, 4, 2

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.

Practica con ejercicios reales

Resuelve ejercicios en el entrenador de SQL con corrección instantánea y pistas.

Abrir el entrenador