¡Hola, chicos!
En este artículo compartiré con ustedes algunos consejos y explicaciones sobre el redondeo de números en SQL Server, usando REDONDEAR, PISO, TECHO y también usando una función personalizada para cumplir con las definiciones del estándar ABNT NBR 5891.
Introducción
Presentes en prácticamente todos los sistemas de información, las funciones de redondeo son muy utilizadas para tratar números fraccionarios y decimales, especialmente con moneda ($). Debido a esta importancia, es muy importante que se comprendan bien las reglas de redondeo para que no haya inconsistencias cuando se aplica el redondeo a números, especialmente en grandes volúmenes de cantidades.
Para cubrir estas necesidades, SQL Server nos proporciona 3 funciones de redondeo:
- PISO: Devuelve un número entero, siempre redondeado hacia abajo, es decir, devuelve la parte entera del número decimal ingresado
- TECHO: Devuelve un valor entero, siempre redondeando hacia arriba, es decir, la parte entera + 1 del número decimal ingresado (si el valor decimal es > 0)
- REDONDEAR: Devuelve un valor decimal, redondeando según el número de decimales especificado en la función. De forma predeterminada, si el decimal es <= 4, se redondeará hacia abajo. Si el decimal es >= 5, se redondeará hacia arriba.
Este comportamiento se puede cambiar mediante el 3er parámetro de la función, que al ingresar el valor 0 truncará el dato en lugar de redondearlo, es decir, 10.999 con 2 decimales sería 10.99.
Sin embargo, cuando tenemos un sistema que necesita seguir la norma ABNT NBR 5891, tenemos un problema, ya que las funciones anteriores no cumplen con los criterios de la norma, que se define de la siguiente manera:
Reglas de redondeo
Las reglas de redondeo, siguiendo la Norma ABNT NBR 5891, se aplican a los dígitos decimales ubicados en la posición siguiente al número de dígitos decimales que se desea transformar, es decir, si tenemos un número de 4, 5, 6, n dígitos decimales y queremos redondear a 2, se aplicarán estas reglas de redondeo:
Si los siguientes dígitos decimales son menos de 50, 500, 5000…, el anterior no cambia.
Si los siguientes dígitos decimales son mayor que 50, 500, 5000…, el anterior aumenta en una unidad.
Si los siguientes dígitos decimales son igual a 50, 500, 5000…, se aplica lo anterior; si es par, el anterior no cambia; si es impar, el anterior aumenta en uno.
Ejemplos
Al redondear a 2 dígitos decimales debemos tener en cuenta el tercer y cuarto decimal. Así, según las reglas anteriores:
El numero 12.6529 se redondearía a 12,65 (aquí es 12,65, ya que 29 es menor que 50, por lo que no cambia)
El numero 12.86512 se redondearía a 12,87 (aquí es 12,87, ya que 512 es mayor que 500, entonces aumenta una unidad)
El numero 12.744623 se redondearía a 12,74 (aquí es 12,74, ya que 4623 es menor que 5000, por lo que no cambia)
El numero 12.8752 se redondearía a 12,88 (aquí es 12,88, ya que 52 es mayor que 50, entonces aumenta una unidad)
El numero 12.8150 se redondearía a 12,82 (aquí es 12.82, ya que los siguientes dígitos son iguales a 50 y el anterior es impar, en este caso 1, luego aumenta en uno)
El numero 12.8050 se redondearía a 12,80 (aquí es 12.80, ya que los siguientes dígitos son iguales a 50 y el anterior es par, en este caso 0, por lo que el anterior no cambia)
El numero 13.4666…, si redondeáramos a la parte entera, siempre se redondeará a 13, ya que 4666… siempre será menor que 5000… (Si redondeamos número a número, tendríamos: 13.4666… → 13.47 → 13.5 → 14, sin embargo, esto sería decir que 13.4666… está más cerca de 14 que el cual es 13, lo cual no es cierto. Por lo tanto, ¡¡¡no debemos redondear el número que ya ha sido redondeado!!!)
Referencia: https://pt.wikipedia.org/wiki/Arredondamento
Redondear números con SUELO, REDONDO y TECHO
Como se mencionó en la introducción de este artículo, SQL Server nos proporciona 3 funciones para el redondeo:
- PISO: Devuelve un número entero, siempre redondeado hacia abajo, es decir, devuelve la parte entera del número decimal ingresado
- TECHO: Devuelve un valor entero, siempre redondeando hacia arriba, es decir, la parte entera + 1 del número decimal ingresado (si el valor decimal es > 0)
- REDONDEAR: Devuelve un valor decimal, redondeando según el número de decimales especificado en la función. De forma predeterminada, si el decimal es <= 4, se redondeará hacia abajo. Si el decimal es >= 5, se redondeará hacia arriba.
Este comportamiento se puede cambiar mediante el 3er parámetro de la función, que al ingresar el valor 0 truncará el dato en lugar de redondearlo, es decir, 10.999 con 2 decimales sería 10.99.
Demostraré algunos ejemplos para que sea más fácil comprender la diferencia entre estas funciones:
SELECT
CAST(CEILING(10.4925) AS NUMERIC(18, 2)) AS [Ceiling],
CAST(FLOOR(10.4925) AS NUMERIC(18, 2)) AS [Floor],
ROUND(10.4925, 2) AS [Round]
SELECT
CAST(CEILING(10.0001) AS NUMERIC(18, 2)) AS [Ceiling],
CAST(FLOOR(10.0001) AS NUMERIC(18, 2)) AS [Floor],
ROUND(10.0001, 2) AS [Round]
SELECT
CAST(CEILING(10.5000) AS NUMERIC(18, 2)) AS [Ceiling],
CAST(FLOOR(10.5000) AS NUMERIC(18, 2)) AS [Floor],
ROUND(10.5000, 2) AS [Round]
En este ejemplo, usaremos el tercer parámetro de la función ROUND() para forzar el redondeo hacia arriba (0 = redondeo estándar, es decir, 0 a 4 redondeos hacia abajo y 5 a 9 redondeos hacia arriba) y el truncamiento (1 = Truncamiento, es decir, no redondea, simplemente corta los decimales por encima del límite especificado en la función).
SELECT
CAST(CEILING(10.9999) AS NUMERIC(18, 2)) AS [Ceiling],
CAST(FLOOR(10.9999) AS NUMERIC(18, 2)) AS [Floor],
ROUND(10.9999, 2) AS [Round],
CONVERT(DECIMAL(10,2), ROUND(10.9999, 2, 1)) AS Round_Down,
CONVERT(DECIMAL(10,2), ROUND(10.9999, 2, 0)) AS Round_Up
SELECT
CAST(CEILING(10.235) AS NUMERIC(18, 2)) AS [Ceiling],
CAST(FLOOR(10.235) AS NUMERIC(18, 2)) AS [Floor],
ROUND(10.235, 2) AS [Round],
CONVERT(DECIMAL(10,2), ROUND(10.235, 2, 1)) AS Round_Down,
CONVERT(DECIMAL(10,2), ROUND(10.235, 2, 0)) AS Round_Up
SELECT
CAST(CEILING(10.225) AS NUMERIC(18, 2)) AS [Ceiling],
CAST(FLOOR(10.225) AS NUMERIC(18, 2)) AS [Floor],
ROUND(10.225, 2) AS [Round],
CONVERT(DECIMAL(10,2), ROUND(10.225, 2, 1)) AS Round_Down,
CONVERT(DECIMAL(10,2), ROUND(10.225, 2, 0)) AS Round_Up
Como puedes ver, la función ROUND() cumple con 2 de las 3 reglas definidas en el estándar ABNT, pero en el ejemplo del valor 10.225, según el estándar, el redondeo debería haberse hecho a 10,22 y no a 10,23.
Redondeo de números siguiendo la norma ABNT NBR 5891
Como vimos en los ejemplos anteriores, cuando caemos en la regla “Si las siguientes cifras decimales son iguales a 50, 500, 5000…, se verifica la anterior; si es par, la anterior no cambia; si es impar, se aumenta en una unidad la anterior.”, las funciones de redondeo estándar de SQL Server terminan por no satisfacer esta necesidad.
Para ello, proporcionaré la siguiente función, que puede cumplir plenamente con las definiciones de redondeo de la norma ABNT NBR 5891:
CREATE FUNCTION dbo.fncArredondamento_ABNT (
@Valor NUMERIC(38, 16)
)
RETURNS NUMERIC(38, 16)
AS
BEGIN
DECLARE
@Parte_Inteira INT = ROUND(@Valor, 0, 1),
@Parte_Decimal NUMERIC(38, 16) = @Valor - ROUND(@Valor, 0, 1),
@Nova_Parte_Decimal NUMERIC(38, 16),
@SegundoDecimal INT,
@DoisPrimeirosDecimais AS NUMERIC(18, 2),
@RestanteDosDecimais AS NUMERIC(38, 16)
SELECT
@SegundoDecimal = SUBSTRING(CAST(@Parte_Decimal AS VARCHAR(40)), 4, 1),
@DoisPrimeirosDecimais = '0.' + SUBSTRING(CAST(@Parte_Decimal AS VARCHAR(40)), 3, 2),
@RestanteDosDecimais = '0.' + SUBSTRING(CAST(@Parte_Decimal AS VARCHAR(40)), 5, 16)
SELECT
@Nova_Parte_Decimal = (CASE
WHEN @RestanteDosDecimais > 0.5 THEN @DoisPrimeirosDecimais + 0.01
WHEN @RestanteDosDecimais < 0.5 THEN @DoisPrimeirosDecimais
ELSE @DoisPrimeirosDecimais + IIF(@SegundoDecimal % 2 = 0, 0.00, 0.01)
END)
RETURN (@Parte_Inteira + @Nova_Parte_Decimal)
END
Ejemplos de uso
El número 12,6529 se redondearía a 12,65 (aquí es 12,65, ya que 29 es menor que 50, por lo que no cambia)
SELECT
CAST(CEILING(12.6529) AS NUMERIC(18, 2)) AS [Ceiling],
CAST(FLOOR(12.6529) AS NUMERIC(18, 2)) AS [Floor],
ROUND(12.6529, 2) AS [Round],
CONVERT(DECIMAL(10,2), ROUND(12.6529, 2, 1)) AS Round_Down,
CONVERT(DECIMAL(10,2), ROUND(12.6529, 2, 0)) AS Round_Up,
dbo.fncArredondamento_ABNT(12.6529) AS [Round_ABNT]
El número 12,86512 se redondearía a 12,87 (aquí es 12,87, ya que 512 es mayor que 500, así que aumenta una unidad)
SELECT
CAST(CEILING(12.86512) AS NUMERIC(18, 2)) AS [Ceiling],
CAST(FLOOR(12.86512) AS NUMERIC(18, 2)) AS [Floor],
ROUND(12.86512, 2) AS [Round],
CONVERT(DECIMAL(10,2), ROUND(12.86512, 2, 1)) AS Round_Down,
CONVERT(DECIMAL(10,2), ROUND(12.86512, 2, 0)) AS Round_Up,
dbo.fncArredondamento_ABNT(12.86512) AS [Round_ABNT]
El número 12,744623 se redondearía a 12,74 (aquí es 12,74, ya que 4623 es menor que 5000, por lo que no cambia)
SELECT
CAST(CEILING(12.744623) AS NUMERIC(18, 2)) AS [Ceiling],
CAST(FLOOR(12.744623) AS NUMERIC(18, 2)) AS [Floor],
ROUND(12.744623, 2) AS [Round],
CONVERT(DECIMAL(10,2), ROUND(12.744623, 2, 1)) AS Round_Down,
CONVERT(DECIMAL(10,2), ROUND(12.744623, 2, 0)) AS Round_Up,
dbo.fncArredondamento_ABNT(12.744623) AS [Round_ABNT]
El número 12,8752 se redondearía a 12,88 (aquí es 12,88, ya que 52 es mayor que 50, así que aumenta en una unidad)
SELECT
CAST(CEILING(12.8752) AS NUMERIC(18, 2)) AS [Ceiling],
CAST(FLOOR(12.8752) AS NUMERIC(18, 2)) AS [Floor],
ROUND(12.8752, 2) AS [Round],
CONVERT(DECIMAL(10,2), ROUND(12.8752, 2, 1)) AS Round_Down,
CONVERT(DECIMAL(10,2), ROUND(12.8752, 2, 0)) AS Round_Up,
dbo.fncArredondamento_ABNT(12.8752) AS [Round_ABNT]
El número 12.8150 se redondearía a 12.82 (aquí es 12.82, ya que los siguientes dígitos son iguales a 50 y el anterior es impar, en este caso 1, por lo que se aumenta una unidad)
SELECT
CAST(CEILING(12.8150) AS NUMERIC(18, 2)) AS [Ceiling],
CAST(FLOOR(12.8150) AS NUMERIC(18, 2)) AS [Floor],
ROUND(12.8150, 2) AS [Round],
CONVERT(DECIMAL(10,2), ROUND(12.8150, 2, 1)) AS Round_Down,
CONVERT(DECIMAL(10,2), ROUND(12.8150, 2, 0)) AS Round_Up,
dbo.fncArredondamento_ABNT(12.8150) AS [Round_ABNT]
El número 12.8050 se redondearía a 12.80 (aquí es 12.80, ya que los siguientes dígitos son iguales a 50 y el anterior es par, en este caso 0, por lo que el anterior no cambia)
SELECT
CAST(CEILING(12.8050) AS NUMERIC(18, 2)) AS [Ceiling],
CAST(FLOOR(12.8050) AS NUMERIC(18, 2)) AS [Floor],
ROUND(12.8050, 2) AS [Round],
CONVERT(DECIMAL(10,2), ROUND(12.8050, 2, 1)) AS Round_Down,
CONVERT(DECIMAL(10,2), ROUND(12.8050, 2, 0)) AS Round_Up,
dbo.fncArredondamento_ABNT(12.8050) AS [Round_ABNT]
¡Bueno, eso es todo, amigos!
Espero que hayas disfrutado de esta publicación y ¡hasta la próxima!












Comentários (0)
Carregando comentários…