El redondeo es una transformación de un número que afecta a su expresión en el sistema de numeración pudiendo llegar incluso a eliminar los decimales. Todo número \(x\) está entre dos enteros consecutivos \(n \le x \lt n + 1\). La parte entera de \(x\) por defecto (parte entera inferior) es el entero \(n\) y la parte entera por exceso (parte entera superior), el entero \(n + 1\)\(\require {AMSmath}\def\keybox#1{\; \boxed {\vphantom {|} #1 \, } \;}\DeclareMathOperator{\sgn}{sgn\,}\). Esto se simboliza como \(\lfloor x \rfloor = n\) y \(\lceil x \rceil = n + 1,\) que se expresa diciendo que \(\lfloor x \rfloor\) es el mayor entero que no supera a \(x\), así como \(\lceil x \rceil\) es el menor entero que no es superado por \(x\). Las identidades siguientes son útiles $$\begin{align}\left\lceil x \right\rceil – \left\lfloor x \right\rfloor &= 1 \\[1.2ex] \left\lfloor x \right\rfloor + \left\lceil -x \right\rceil &= 0 \tag{1}\label{eq1}\end{align}$$
Conocer la parte entera de un número permite conocer una aproximación de éste. Siempre tendremos que \(\lfloor x \rfloor \le x \lt \lfloor x \rfloor + 1\), por lo que \(\lfloor x \rfloor\) es una aproximación de \(x\) que difiere de éste en menos de una unidad \(0 \le x -\lfloor x \rfloor \lt 1\). Más aún, \( \lfloor x \rfloor + \frac{1}{2}\) es mejor aproximación de \(x\) pues difieren en no más de \(\frac{1}{2}.\)
Para cualquier número \(x\) es posible escribir \(x = \lfloor x \rfloor + f\), como suma de su parte entera (por defecto) y el número \(f\) no negativo y menor que la unidad que es la parte fraccionaria de \(x\). Así $$15{,}257 = 15 + 0{,}257 \qquad \text{y} \qquad -15,257 = -16 + 0{,}743$$Este resultado puede verse como consecuencia de la división entera cuando el divisor es la unidad. En el artículo División entera se presentó el concepto de división entera de \(a\) entre \(b\) (aquí suponemos \(b\) positivo). $$a = b \cdot q + r, \qquad\text{con } 0 \le r \lt b \tag{2}\label{eq2}$$Si, ahora, dividimos los dos lados de la igualdad anterior entre el divisor, obtenemos una expresión del número racional \({a / b}\): $$\frac{a}{b} = q + \frac{r}{b}, \qquad\text{con } 0 \le \frac{r}{b} \lt 1$$ El cociente \(q\) es la parte entera (por defecto) de la fracción \(\dfrac{a}{b}\), es decir $$q = \left \lfloor {a/b} \right \rfloor$$ y el sumando \(\dfrac{r}{b}\) es la parte decimal de la fracción \(\dfrac{a}{b}\). Con esta notación el resto de la división entera lo podemos expresar como:$$r = a -b \cdot \left \lfloor {a / b} \right\rfloor$$ Este resto se suele denotar utilizando el operador módulo: \(a \bmod b\), (leido «\(a\) módulo \(b\)») con lo que la división entera la podemos expresar como $$a = b \cdot \left\lfloor {a / b} \right\rfloor + \left ( a \bmod b \right )$$
Siempre hay que fijarse en que los números \((-8)/5\) y \(8/(-5)\) son iguales a \(-(8/5) = -1{,}6\), pero, las divisiones enteras no lo son $$-8 = 5 \cdot (-2) + 2, \qquad 8 = (-5) \cdot (-1) + 3$$ Lo mismo puede decirse de las fracciones \((-8)/(-5)\) y \(8/5\), son iguales pero las divisiones enteras, no $$8 = 5 \cdot 1 + 3, \qquad -8 = (-5) \cdot 2 + 2$$
Todos no hablan de lo mismo
Es habitual encontrar en las calculadoras la tecla \(\keybox{\bmod}\) cuya función es obtener el resto de una división entera. Esta operación suele incluirse también en aplicaciones de teléfono móvil y todos los lenguajes de programación de computadoras la incluyen en la lista de operadores o como función, con nombres o símbolos muy diversos. Así, podemos calcular $$17 \keybox{\bmod} 5 \keybox{=} 2$$ Sin embargo hay que vigilar el resultado cuando se trabaja con números negativos porque puede obtenerse un valor distinto del resto de la división entera: ¡La calculadora de Windows \(10\) da como resultado para \(-17 \keybox{\bmod} 5\) el valor \(-2\) y no \(3\) como debería obtenerse para la división entera!
Estas diferencias de implementación aparecen también en los lenguajes de programación. En Python, cuando el divisor \(b\) es positivo, los operadores //
y %
y la función divmod(a, b)
dan cociente y resto de la división entera pero, si \(b\) es negativo, no devuelven los valores correctos. $$\def\doblebarra {\mathbin{/ \negthickspace /}} \def\smallpct{\mathbin {\small {\%}}} a \doblebarra b \equiv \lfloor a / b \rfloor, \quad a \smallpct b \equiv a -b \cdot ( a \doblebarra b)$$
La parte fraccionaria de \(x\) puede calcularse como \(\text{pfrac}(x) = x -\lfloor x \rfloor = x \smallpct 1\)
def pfrac(num): """Parte fraccionaria de num, siempre positiva. = num % 1 """ return num - math.floor(num) def spfrac(num): """Parte fraccionaria con el signo de num = num - math.floor(num) si num < 0 num - math.ceil(num) si num <= 0 """ return sgn(num) * pfrac(abs(num))
Lo habitual es que el divisor sea positivo, pero, si es negativo, Python necesita un pequeño ajuste en el cálculo del cociente. Utiliza la función divmod_mat()
siguiente que da los valores «matemáticos» cualquiera que sea el signo del divisor.
def divmod_mat(a, b): """División entera matemática """ div = sgn(b) * (a // abs(b)) mod = a % abs(b) return (div, mod)
La expresión que calcula el cociente div
(en la línea 3) utiliza la función signo, que también será utilizada a lo largo de este artículo.
def sgn(num): """Signo de un número -1 si num < 0 0 si num == 0 +1 si num > 0 """ return (num > 0) - (num < 0)
$$\begin{array}{r|r|c|c|} a & b & \text{divmod}(a, b) & \text{divmod_mat}(a, b) \\ \hline 8 & 5 & (1, 3) & (1, 3)\\ \hdashline -8 & 5 & (-2, 2) & (-2, 2)\\ \hdashline 8 & -5 & (\color{red} {-2}, \color{red} {-2}) & (-1, 3) \\ \hdashline -8 & -5 & (\color{red} {1}, \color{red} {-3}) & (2, 2) \end{array}$$
Pero vayamos a lo que nos ocupa en este artículo, los redondeos. Todos los sistemas de cálculo y lenguajes de programación de computadoras tienen predefinidas varias funciones que calculan redondeos. Con ellas podremos definir nuestras propias funciones de redondeo. Las variantes de los métodos de redondeo son numerosas y su implementación en los sistemas de cálculo y lenguajes de programación pueden tener ligeras diferencias. Simultanearemos expresiones en notación matemática y definiciones en Python 3.
Redondeos a entero
Básicamente una función de redondeo realiza una transformación de las cifras de un número para obtener un valor aproximado del mismo. Podemos empezar con los métodos de redondeo a entero para seguir con los redondeos sobre los decimales. Para simplificar algunas expresiones utilizaremos la identidad \(\eqref{eq1}\) que relaciona las partes enteras inferior y superior. Estas funciones básicas se corresponden con las funciones Python math.floor()
y math.ceil()
, respectivamente. Este lenguaje posee otra función de redondeo round()
que analizaremos más adelante. Es suficiente con estas funciones básicas para desarrollar nuestras propias funciones de redondeo:
- entero por defecto (o inferior): \(\left\lfloor x \right\rfloor\)
- entero por exceso (o superior): \(\left\lceil x \right\rceil\)
- truncamiento: $$\sgn (x) \cdot \left\lfloor \, \left \lvert x \right\rvert \, \right\rfloor = \begin{cases} \left\lceil x \right\rceil & \text{si } x \le 0 \\ \left\lfloor x \right\rfloor & \text{si } x \gt 0 \end{cases}$$
El truncamiento consiste literalmente en «borrar» los decimales. En adelante se ha nombrado con el prefijo entero_
a todos los redondeos que devuelven números enteros, salvo la función trunca()
, y con redondeo_
a los que devuelven números con decimales. Las definiciones de las funciones en Python contienen entre comillas triples """..."""
una pequeña documentación de la función entre la que se encuentra un cálculo alternativo que puedes probar sustituyendo la expresión detrás de la palabra return
# Importar el módulo math para utilizar las funciones # math.floor() y math.ceil() # import math def entero_s(num): """Entero por exceso = -math.floor(-num) """ return math.ceil(num) def entero_i(num): """Entero por defecto = -math.ceil(-num) """ return math.floor(num) def trunca(num): """Omite los decimales """ return sgn(num) * math.floor(abs(num))
En la tabla siguiente se muestra cómo se redondea en intervalos de negativos como \([-1, 0]\) y en intervalos de positivos como \([0, 1]\). Las flechas señalan el entero de la izquierda o la derecha que será el resultado del cálculo. La parte entera por exceso redondea hacia \(\bf {+\infty} \), la parte entera por defecto lo hace hacia \(\bf {-\infty} \) y el truncamiento redondea hacia \(\bf {0}\).
$$\small {\begin{array}{c|c|c|c|} x & -1 & \dotsb & -0{,}5 & \dotsb & 0 & \dotsb & \;0{,}5\; & \dotsb & \;1\; \\ \hline \text{entero_s} (x), \left\lceil x \right\rceil & -1 & \rightarrow & \rightarrow & \rightarrow & \;0\; & \rightarrow & \rightarrow & \rightarrow & \;1\; \\ \hdashline \text{entero_i}(x), \left\lfloor x \right\rfloor& -1 & \leftarrow & \leftarrow & \leftarrow & \;0\; & \leftarrow & \leftarrow & \leftarrow & \;1\; \\ \hdashline \text{trunca}(x) & -1 & \rightarrow & \rightarrow & \rightarrow & \;0\; & \leftarrow & \leftarrow & \leftarrow & \;1\; \\ \hline \end{array} }$$