Saltar al contenido principal
LibreTexts Español

4.6: Números de coma flotante (FPN): Representación y operaciones

  • Page ID
    87580
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)

    \( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)

    ( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)

    \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)

    \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)

    \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)

    \( \newcommand{\Span}{\mathrm{span}}\)

    \( \newcommand{\id}{\mathrm{id}}\)

    \( \newcommand{\Span}{\mathrm{span}}\)

    \( \newcommand{\kernel}{\mathrm{null}\,}\)

    \( \newcommand{\range}{\mathrm{range}\,}\)

    \( \newcommand{\RealPart}{\mathrm{Re}}\)

    \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)

    \( \newcommand{\Argument}{\mathrm{Arg}}\)

    \( \newcommand{\norm}[1]{\| #1 \|}\)

    \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)

    \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    \( \newcommand{\vectorA}[1]{\vec{#1}}      % arrow\)

    \( \newcommand{\vectorAt}[1]{\vec{\text{#1}}}      % arrow\)

    \( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vectorC}[1]{\textbf{#1}} \)

    \( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)

    \( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)

    \( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)

    \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)

    Truncamiento y Representación

    Los números de coma flotante representan un desafío tanto en cómo representamos estos números como en cómo realizamos operaciones aritméticas sobre estos números. Para comenzar, expresamos un número\(x\) en base 2 como\[x=\sigma_{1}\left(\sum_{k=0}^{\infty} b_{k} 2^{-k}\right) \times 2^{\sigma_{2} \mathbb{E}},\] en el que los\(b_{k}\) son números binarios\(-0\) o\(1, \mathbb{E}\) es un entero, y\(\sigma_{1}\) y\(\sigma_{2}\) son signos\(-\pm 1\). Suponemos que hemos normalizado la expansión de tal manera que\(b_{0}=1\). (De hecho, podemos expresar\(x\) en digamos base 10 en lugar de base 2; esto a su vez conducirá a un formato de punto flotante diferente).

    En algunos casos, es posible que solo necesitemos una suma finita -una suma con un número finito de términos distintos de cero- para representar\(x\). Por ejemplo,\(x=2\) puede expresarse por el único término distinto de cero\(b_{0}=1\) (y\(\mathbb{E}=+1)\). Sin embargo, de manera más general, un número finito de distinto de cero no\(b_{k}\) será suficiente, incluso\(1 / 10\) conduce a una fracción binaria repetida. Por lo tanto, debemos truncar la serie para desarrollar la aproximación del número de punto flotante (FPN) de\(x\):\[x_{\mathrm{FPN}}=\sigma_{1}\left(\sum_{k=0}^{K} b_{k}^{\prime} 2^{-k}\right) \times 2^{\sigma_{2} \mathbb{E}^{\prime}}\] Aquí\(b_{k}^{\prime}=b_{k}, 1 \leq k \leq K\) - realizamos truncamiento de nuestra serie - y\(\mathbb{E}^{\prime}\) es el mínimo de\(\mathbb{E}\) y\(\mathbb{E}_{\max }\) - nosotros trunca el rango del exponente.

    Ahora representamos o codificamos\(x_{\mathrm{FPN}}\) en términos de (un número finito de) 0 y 1. Hacia este fin asignamos un bit cada uno a los signos\(\sigma_{1}\) y\(\sigma_{2}\); asignamos\(p=K\) bits para los números binarios\(b_{k}^{\prime}\), \(1 \leq k \leq K\), para representar la mantisa (o significando); asignamos\(p_{\mathbb{E}}\) bits para representar el exponente\(\mathbb{E}\) (y por lo tanto\(\mathbb{E}_{\max }=2^{p_{\mathbb{E}}}\)). (Nuestra elección de la base 2 hace que la codificación de nuestra aproximación en 0 y 1 sea particularmente simple). En el formato de punto flotante doble binario IEEE 754 de 64 bits (ahora llamado binario64),\(p=52\) y\(p_{\mathbb{E}}=10\) (correspondiente a\(\mathbb{E}_{\max }=310\) tal que en total -incluyendo los bits de signo- requerimos\(2+52+10=64\) bits. (El esquema de almacenamiento realmente implementado en la práctica es ligeramente diferente: no necesitamos almacenar el bit de unidad líder y, por lo tanto, nos damos cuenta efectivamente\(p=53\); el signo del exponente\(\sigma_{2}\) se representa de hecho como un cambio). Hay dos fuentes primarias o tipos de error en la aproximación de\(x\) by\(x_{\mathrm{FPN}}\): la primera es truncamiento FPN de la mantisa a\(p\) bits; la segunda es el truncamiento FPN del exponente a\(p_{\mathbb{E}}\) bits. El primero, truncamiento de mantisa FPN, es generalmente bastante benigno dado el valor bastante grande de\(p\). Sin embargo, en algunos casos, los errores de truncamiento de mantisa FPN pueden amplificarse seriamente mediante operaciones aritméticas. Este último, truncamiento de exponente FPN, toma la forma de exponentes de desbordamiento mayores que 310, representados en MATLAB como más o menos Inf, que suele ser una indicación de mala disposición, o subdesbordamiento, exponentes menores que\(-310\), representados en MATLAB como 0, que normalmente es menor de una preocupación.

    Observamos que la palabra “precisión” suele estar reservada para indicar el número de bits o dígitos con los que se aproxima un número de coma flotante en cualquier hardware particular (y formato IEEE); típicamente nos enfocamos en la mantisa. Por ejemplo, la precisión de 64 bits, o “doble precisión”, corresponde a 52 (o 53) dígitos binarios de precisión, aproximadamente 16 dígitos decimales de precisión en la mantisa. La precisión también se puede caracterizar en términos de “precisión de máquina” o “máquina épsilon” que es esencialmente la magnitud (relativa) del error de truncamiento FPN en el peor de los casos: podemos encontrar epsilon de máquina a partir de la función incorporada eps de MATLAB, como ilustraremos a continuación. Definiremos la máquina épsilon con mayor precisión, y posteriormente construiremos un código para encontrar una aproximación a la máquina épsilon, una vez que hayamos entendido la aritmética de coma flotante.

    A menudo analizaremos un esquema numérico en aritmética hipotética de “precisión infinita” para comprender los errores debidos a la aproximación numérica y la solución en ausencia de efectos de truncamiento FPN de precisión finita. Pero siempre debemos tener en cuenta que en la aritmética de precisión finita se incurrirán errores adicionales debido a la amplificación de los errores de truncamiento de FPN por diversas operaciones aritméticas. En breve discutimos este último en particular para identificar los tipos de operaciones que, de ser posible, debemos evitar.

    Finalmente, remarcamos que hay muchas formas en las que podemos elegir mostrar un número digamos en la ventana de comandos. La forma en que mostramos el número no afectará cómo se almacena el número en la memoria ni cómo se aproxima en diversas operaciones. El lector puede hacer formato de\(\gg\) ayuda para comprender las diferentes formas de controlar la longitud de la mantisa y la forma del exponente en los números de punto flotante mostrados. (De manera confusa, el formato en el contexto de cómo mostramos un número tiene un significado diferente al formato en el contexto del protocolo (IEEE) FPN).

    Operaciones Aritméticas

    Nos enfocaremos en la adición ya que de hecho esta operación particular (simple) es la causa de la mayoría de las dificultades. Consideraremos dos números\(x_{1}\) y los\(x_{2}\) que deseamos agregar: el primer número tiene mantisa\(m_{1}\) y exponente\(\mathbb{E}_{1}\) y el segundo número tiene mantisa\(m_{2}\) y exponente\(\mathbb{E}_{2}\). Presumimos que\(\mathbb{E}_{1}>\mathbb{E}_{2}\) (si no, simplemente redefinimos “primero” y “segundo”).

    Primero, dividimos la primera mantisa por\(2^{\mathbb{E}_{1}-\mathbb{E}_{2}}\) para obtener\(m_{2}^{\prime}=m_{2} 2^{-\left(\mathbb{E}_{1}-\mathbb{E}_{2}\right)}\): en esta forma,\(x_{1}\) ahora tiene mantisa\(m_{2}^{\prime}\) y exponente\(\mathbb{E}_{1}\). (Tenga en cuenta que esta división corresponde a un desplazamiento de la mantisa: para obtener\(m_{2}^{\prime}\) nos desplazamos\(m_{1}\) por\(\mathbb{E}_{1}-\mathbb{E}_{2}\) lugares a la derecha - y pad con ceros a la izquierda.) En esta etapa no hemos perdido ninguna precisión. Sin embargo, en la práctica real solo podemos retener los primeros\(p\) bits de\(m_{2}^{\prime}\) (ya que solo tenemos\(p\) bits disponibles para una mantisa): denotamos por\(m_{1}^{\prime \prime}\) el truncamiento de\(m_{1}\) para encajar dentro de nuestro \(p\)-restricción de bits. Por último, realizamos nuestra suma FPN\(z=x_{1}+x_{2}: z\) tiene mantisa\(m_{1}+m_{2}^{\prime \prime}\) y exponente\(\mathbb{E}_{1}\). (Nuestro procedimiento aquí es una simplificación del procedimiento real, pero conservamos la mayoría de las características clave).

    Podemos ver de inmediato la dificultad: a medida que nos desplazamos\(m_{2}\) hacia la derecha estamos perdiendo\(\mathbb{E}_{1}-\mathbb{E}_{2}\) pedacitos de precisión. Si los dos exponentes\(\mathbb{E}_{1}\) y\(\mathbb{E}_{2}\) son muy diferentes, podríamos perder todos los dígitos significativos en\(x_{2}\). Armados con FPN podemos de hecho desarrollar una definición simple de epsilon de máquina: el épsilon más pequeño tal que\(1+\) épsilon\(=1\), donde por supuesto por + ahora nos referimos a la adición de FPN de precisión finita. Posteriormente aprovecharemos esta definición para escribir un programa corto que compute la máquina epsilon; para nuestros fines aquí, simplemente usaremos la función incorporada eps de MATLAB.

    Está claro que la aritmética de precisión finita y precisión infinita son diferentes y producirán resultados diferentes; la diferencia se conoce comúnmente como error de “redondez”. En efecto, la artmética de precisión finita ni siquiera honra todas las reglas habituales (e.g., conmutativa, asociativa). Consideramos el ejemplo (recordemos que en MATLAB las operaciones se realizan de izquierda a derecha en ausencia de reglas de precedencia):

    >> mach_eps = eps 
    mach_eps =
        2.2204e-16
    >> (mach_eps/2 + 1 + mach_eps/2 - 1)/mach_eps
    ans =
        0
    >> (mach_eps/2 + mach_eps/2 + 1 - 1)/mach_eps
    ans =
        1
    >>
    

    Claramente, en aritmética de precisión infinita ambas expresiones deben evaluar a la unidad. Sin embargo, en precisión finita el orden importa: en la primera expresión por definición mach_eps\(/ 2+1\) evalúa a 1; en la segunda expresión, mach_eps/2 + mach_eps/2 agrega dos números de exponente idéntico -sin pérdida de precisión- que entonces son lo suficientemente grandes (¡solo!) para sobrevivir además de 1. Esta anomalía es un “error” pero también puede ser una característica: a veces podemos ordenar nuestras operaciones para reducir el efecto de

    Pero hay situaciones que son bastante difíciles de salvar. En el siguiente ejemplo aproximamos la derivada de\(\sin (x)\) por una diferencia de primer orden hacia adelante con un incremento\(\mathrm{dx}\) que es cada vez más pequeño:

    >> cos(pi/4)
    ans =
        0.707106781186548
    >> dx = .01;
    >> deriv_dx = (sin(pi/4 + dx) - sin(pi/4))/dx
    deriv_dx =
        0.703559491689210 63
    >> dx = 1e-8;
    >> deriv_dx = (sin(pi/4 + dx) - sin(pi/4))/dx
    deriv_dx =
        0.707106784236800
    >> dx = 1e-20;
    >> deriv_dx = (sin(pi/4 + dx) - sin(pi/4))/dx
    deriv_dx =
        0
    >>
    

    Observamos que lo que Newton pretendía -y el límite de error que presentamos en el Capítulo 3- se honra efectivamente: como dx tiende a cero, la diferencia finita (pendiente) se acerca a la derivada (cos\((\pi / 4)\)). Pero no del todo: como dx cae por debajo de la precisión de la máquina, el numerador ya no puede ver la diferencia, y obtenemos un\(O(1)\) error -precisamente en el límite en el que deberíamos ver una respuesta cada vez más precisa. (Como el lector sin duda puede adivinar, pi, sin y cos son todas funciones integradas en MATLAB).

    Esto es de hecho un comportamiento muy típico. Para que los errores numéricos sean pequeños debemos tomar incrementos más pequeños o muchos grados de libertad, sin embargo si vamos “demasiado lejos” entonces los efectos de precisión finita lamentablemente “patean”. De hecho, esta compensación podría ser debilitante si la precisión de la máquina no fuera lo suficientemente pequeña, y de hecho en los primeros días de la computación con solo unos pocos bits para representar FPN era una lucha equilibrar la precisión numérica con efectos de redondez de precisión finita. En estos días, con el lujo de una precisión de 64 bits, los errores de redondez son algo menos preocupantes. Sin embargo, hay situaciones en las que los efectos de redondez pueden llegar a ser importantes.

    En particular, observamos que el problema es nuestro ejemplo derivado no es sólo el numerador sino también el\(d x\) en el denominador. Como regla general, deseamos evitar -cuando sea posible- la división por números pequeños, lo que tiende a amplificar los efectos del truncamiento de precisión finita. (Esto se relaciona con la estabilidad, que es un tema importante que encontraremos en muchas formas, a menudo relacionadas, en capítulos posteriores). Veremos que incluso en ejemplos mucho más sofisticados -solución de grandes sistemas lineales- “evitar la división por números pequeños” sigue siendo una pauta importante y muchas veces una característica (por construcción) de buenos algoritmos. El problema por supuesto se agrava cuando debemos realizar muchas operaciones en contraposición a solo unas pocas operaciones.

    Hemos centrado nuestra atención en la suma ya que, como se indica, esta operación suele ser la causa proximal de las dificultades de redondez. Otras operaciones se realizan de la manera “obvia”. Por ejemplo, para multiplicar dos números, multiplicamos las mantisas y sumamos los exponentes y luego reajustamos para conformarnos a la representación necesaria. La división y la exponenciación siguen recetas similares. Obviamente, el desbordamiento y el desbordamiento pueden ser subproductos no deseados, pero estos suelen ser más fáciles de evitar y no “fundamentales”.


    This page titled 4.6: Números de coma flotante (FPN): Representación y operaciones is shared under a CC BY-NC-SA 4.0 license and was authored, remixed, and/or curated by Masayuki Yano, James Douglass Penn, George Konidaris, & Anthony T Patera (MIT OpenCourseWare) via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.