---
tags: geometry, precision, error, tolerance
---
# Precisión
## Manejo de números reales
En geometría usaremos mucho el conjunto de los números reales $\mathbb{R}$. Una forma de simularlos en algún lenguaje de programación es usar números flotantes y en C++ usaremos los tipos de dato ``double`` y ``long double``.
La desventaja es que la precisión de los números reales es en teoría infinita, pero la de los números flotantes es limitada. Eso quiere decir que números como $\sqrt{2}$ o $\pi$, e incluso $0.1$ no podrán ser representados exactamente con un número flotante, y en problemas de geometría estos números aparecerán frecuentemente, ya sea de forma intermedia o como la respuesta final.
### Comparaciones usando $\epsilon$
Cada que comparemos dos números flotantes usando operadores de orden o de igualdad, tales como $<$, $>$, $\leq$, $\geq$, $=$ y $\neq$, no es recomendable usar los operadores directamente en código. En su lugar, hay que introducir el concepto de *tolerancia*, es decir, no seremos tan estrictos al comparar los dos números. La medida de esa tolerancia estará dada por un número positivo *épsilon* ($\epsilon$) muy pequeño, usualmente llamado en código como ``eps``.
En resumen, vamos a transformar las comparaciones a comparaciones contra el cero, y transformar el cero de ser un punto en la recta a ser un rango con longitud muy pequeña, dicho rango será $[-\epsilon, \epsilon]$. De esta forma, todo lo que esté dentro de ese rango será considerado como cero. Por ejemplo, si tomamos $\epsilon=10^{-4}$, cualquier número $x$ será considerado como cero si $-\epsilon \leq x \leq \epsilon$:

Ahora veamos cómo transformar cada comparación:
- Comparaciones de orden:
- Para verificar si $a<b$ podemos reescribir la condición como $a-b < 0$. Eso quiere decir que $a-b$ tiene que estar estrictamente del lado izquierdo de la recta numérica, o sea, sin tocar el $0$. Por lo tanto, $a-b$ tiene que estar a la izquierda del rango $[-\epsilon, \epsilon]$, es decir, $\boxed{a-b < -\epsilon}$.
- Esto nos dice inmediatamente que si $a-b$ no está del lado izquierdo, no se cumple que $a<b$, o sea, se cumple su negación, que es que $a \geq b$. Usando épsilon, entonces $a \geq b$ se transforma como $\boxed{a-b \geq -\epsilon}$.

- Comparaciones de orden:
- Para verificar si $a > b$ es lo mismo que verificar si $b < a$, lo cual ya sabemos hacer.
- Para verificar si $a \leq b$ es lo mismo que verificar si $b \geq a$, igual ya lo sabemos hacer.
- Comparaciones de igualdad:
- Para verificar si $a=b$ podemos reescribir la condición como $a-b = 0$. Esto significa que $a-b$ tiene que estar exactamente en el cero, pero como el cero ahora es un rango, quiere decir que $-\epsilon \leq a-b \leq \epsilon$. Usando la definición de valor absoluto, podemos escribir simplemente $\boxed{\lvert a-b \rvert \leq \epsilon}$.
- Esto nos dice inmediatamente que si $a-b$ no cae dentro del rango de tolerancia, es decir, si $\boxed{\lvert a-b \rvert > \epsilon}$, entonces $a \neq b$.

En la práctica se suelen usar valores de épsilon tales como $\epsilon=10^{-8}$, $\epsilon=10^{-9}$, $\epsilon=10^{-10}$, $\epsilon=10^{-11}$ o $\epsilon=10^{-12}$. Entre más se acerque épsilon a cero más estricta será la comparación, y entre más grande sea, tendrá más tolerancia. En C++ el épsilon se suele declarar como variable global y las comparaciones como funciones, un ejemplo es:
```cpp
const double eps = 1e-9;
// For use with integers, just set eps=0 and everything remains the same
bool geq(double a, double b){return a-b >= -eps;} //a >= b
bool leq(double a, double b){return b-a >= -eps;} //a <= b
bool ge(double a, double b){return b-a < -eps;} //a > b
bool le(double a, double b){return a-b < -eps;} //a < b
bool eq(double a, double b){return abs(a-b) <= eps;} //a == b
bool neq(double a, double b){return abs(a-b) > eps;} //a != b
```

### Error absoluto y relativo
Supongamos que estamos resolviendo un problema cuya respuesta exacta es $x$ pero nosotros obtuvimos $x_{approx}$. Usualmente ese problema tendrá alguna tolerancia al imprimir la salida si $x$ no es un número entero, y obtendremos AC si caemos dentro de esa tolerancia. Hay tres formas de determinar si nuestra respuesta es correcta:
1. Si el *error absoluto*, definido como $\epsilon = \lvert x - x_{approx} \rvert$, cumple que $\epsilon \leq \epsilon_0$, donde $\epsilon_0$ es especificado en el enunciado del problema. Este tipo de comparación que hará el juez es exactamente la misma que definimos anteriormente, solo que se suelen usar $\epsilon_0$ no tan estrictos, de alrededor $10^{-6}$. Este tipo de comparación se dice que es absoluta porque solo mide la distancia entre los dos valores, y significa que si desplazamos ambos un valor de $k$, el error absoluto se mantendrá igual.
3. Si el *error relativo*, definido como $\eta = \dfrac{\epsilon}{\lvert x \rvert} = \dfrac{\lvert x - x_{approx} \rvert}{\lvert x \rvert}$, cumple que $\eta \leq \eta_0$ (aquí asumimos que si $x = 0$, entonces el error relativo tiende a infinito). Este tipo de comparación es relativa, pues solo nos indica qué tan grande es el error absoluto *respecto* al valor original. Es decir, si tanto $x$ como $x_0$ los amplificamos en un factor de $k$, el error absoluto se amplificará también por $k$, pero el error relativo se mantendrá igual.
4. Si el error absoluto **o** el error relativo es menor o igual a $err$, es decir, $\lvert x - x_{approx} \rvert \leq err$ ó $\dfrac{\lvert x - x_{approx} \rvert}{\lvert x \rvert} \leq err$, nuestra respuesta será correcta, donde $err$ lo especifican en el enunciado y también suele ser del orden de $10^{-6}$. Esta es la forma más comun y amigable de determinar si nuestra respuesta es correcta. Una condición equivalente es:
$$
\begin{align}
\lvert x - x_{approx} \rvert \leq err \quad \text{o} \quad \dfrac{\lvert x - x_{approx} \rvert}{\lvert x \rvert} &\leq err \\
\min \left( \lvert x - x_{approx} \rvert, \dfrac{\lvert x - x_{approx} \rvert}{\lvert x \rvert} \right) &\leq err \\
\lvert x - x_{approx} \rvert \min \left( 1, \dfrac{1}{\lvert x \rvert} \right) &\leq err \\
\dfrac{\lvert x - x_{approx} \rvert}{1 / \min \left( 1, \dfrac{1}{\lvert x \rvert} \right)} &\leq err \\
\dfrac{\lvert x - x_{approx} \rvert}{\max \left( 1, \lvert x \rvert \right)} &\leq err
\end{align}
$$
Ejemplos:
- Si $x=115.7654$ y $x_{approx}=115.7621$, el error absoluto es $\epsilon = \lvert 115.7654 - 115.7621 \rvert = 0.0033$ y el relativo es $\eta = \dfrac{0.0033}{115.7654} = 0.0000285$. Vemos que el error relativo fue el menor.
- Si $x=0.001234$ y $x_{approx}=0.001253$, el error absoluto es $\epsilon = \lvert 0.001234 - 0.001235 \rvert = 1.9 \times 10^{-5}$ y el relativo es $\eta = \dfrac{1.9 \times 10^{-5}}{0.001234} \approx 0.015397$. Aquí el error absoluto fue menor.
Lo que se observamos que con valores pequeños (menores a 1, tales como probabilidades) el error relativo tiende a crecer bastante pero el absoluto se mantiene estable, pero con valores grandes el error relativo es estable pero el absoluto crece bastante. Es por eso que la tercer forma de verificar si nuestra respuesta es correcta combina lo mejor de ambos errores, y nos da más tolerancia.
Existe una cuarta forma de verificar si la respuesta es correcta:
4. Imprimir la respuesta con $n$ número de decimales después del punto decimal, redondeada, donde $n$ no suele exceder 4 o 5. Es una forma más estricta y un caso especial de la comparación con error absoluto, pues aquí $\epsilon_0 = 10^{-n}$.
Para imprimir $x$ en C++ con exactamente $n$ decimales después del punto y redondeado, usamos ``cout << fixed << setprecision(n) << x``. Cuando la forma de comparación sea una de las primeras tres y si la tolerancia es de $10^{-n}$, conviene imprimir la respuesta con $n+3$ o $n+4$ decimales después del punto solo para estar seguros.