¿Cuál es el menor número mayor que 0? Esta es una de esas preguntas simples con respuestas complicadas en algún lugar entre «no hay «y»depende».
Si le preguntas a un matemático, te dirá que no puede haber tal número porque rompería las matemáticas. Si tienes un número n, donde n es el número más pequeño después de 0, entonces no puede haber un número n/2, porque n ya es el más pequeño. Esto significa que la división misma se rompe, lo que a los matemáticos no les gusta.
Si le preguntas a una computadora, en realidad obtendrás una respuesta. A diferencia del mundo real, las computadoras no tienen una cantidad infinita de números porque simplemente no cabían. Las computadoras almacenan números en registros de memoria, y cada registro tiene un número fijo de bits. Imagina si solo tuvieras tres dígitos. El número más grande que podrías representar sería 999. Esto es lo que sucede en una computadora.
Esto significa que el conjunto de enteros en una computadora está limitado por el número de dígitos. En una computadora, definitivamente hay un número mayor (por ejemplo, INT_MAX
en C). Es el número con la cantidad máxima de dígitos, todos los cuales se establecen en un binario 1
. En un sistema de 8 bits, este número sería 11111111
.
Podemos complicar las cosas aún más al incluir números negativos. Si tenemos 8 bits de datos, podemos usar el primer bit para representar el signo del número. 0
para más y 1
para menos. Ahora tenemos 7 bits para nuestros dígitos, por lo que el número más grande es 011111111
, que es más pequeño que nuestro número más grande anterior.
Todavía no hemos terminado. También necesitamos representar números decimales. Incluso si 0.12 es un número pequeño, todavía tiene tres dígitos, al igual que 123. La diferencia es que hay una cosa más en la que debemos pensar: el punto decimal, también llamado punto radix. Necesitamos almacenar tanto los dígitos como la posición del punto radix.
Mientras que los enteros están limitados en cuanto a su tamaño, los números decimales están limitados tanto en tamaño como en precisión. Si tienes un número fijo de dígitos, solo hay tantos dígitos que puedes poner después del punto decimal. Esta es la razón por la que las computadoras necesitan redondear números decimales.
¿Cómo almacenamos estos números decimales, entonces? Las computadoras solo entienden enteros, por lo que necesitamos una forma de almacenar un número decimal solo usando enteros.
Digamos que tenemos el número 3.14
. Comencemos por escribir todos los dígitos del número. Obtenemos 314
. Es un comienzo. Sabemos que multiplicando por potencias de 10, podemos «mover» el punto decimal alrededor del número. 314 * 10^-1
es 31.4, mientras que 314 * 10^-2
es 3.14.
Todo lo que necesitamos para representar el número 3.14 son tres enteros: 314, 10 y -2. 314 es lo que se llama una mantisa, y esto es todos los dígitos del número escrito.
10 se denomina radix o base. Sabemos que multiplicando por potencias de 10 podemos mover el punto decimal alrededor de números en base 10. Las mismas obras para todas las bases numéricas: en base 2 (o binario), puede cambiar el punto multiplicando por potencias de 2.
La potencia por la que cambiamos se llama exponente, y nos dice dónde está el punto decimal.
Puede escribir cada número decimal como esos tres números con una fórmula simple:
number = significand * base^exponent3.14 = 314 * 10^-2
32.8 = 328 * 10^-1
Una computadora almacena un número decimal almacenando el signo, el exponente y el significando en una sola cadena de dígitos de 32 o 64 bits. Por lo general, hay 1 bit para el signo, 11 bits para almacenar el exponente y 53 bits para almacenar el significando, sumando hasta 64.
Con eso en mente, volvamos a nuestra pregunta: ¿Cuál es el número distinto de cero más pequeño? Si solo tenemos tres dígitos de sobra, el número más pequeño posible es 0.01. Con cuatro dígitos, es 0.001. Notarás un patrón aquí: el significando es siempre el mismo, solo que el exponente cambia.
Lo que necesitamos es un significando de 1
, porque es el más pequeño después de 0
. Luego necesitamos desplazar el punto decimal lo más lejos que podamos hacia la izquierda. Para hacer esto necesitamos el exponente más pequeño (más negativo) posible.
Cuán pequeño, depende del diseño del número en memoria. Si tenemos 11 bits para el exponente, solo podemos escribir un número que tenga 10 bits de largo, con 1 bit reservado para el signo. En un sistema de 64 bits, el exponente más pequeño es -308
.
Al final, el número más pequeño posible en un sistema de 64 bits sería alrededor de 1 * 10^-308
. Que pequeño!
Establecimos que hay un número más pequeño. Este número nos dice cuánto podemos confiar en nuestra computadora. Si está haciendo algo que requiere números muy grandes o números muy precisos, debe tener en cuenta este número.
Lo que acabamos de calcular es algo llamado la unidad en el último lugar, o ulp, de 0. Además de ser una palabra realmente genial, ulp nos dice cuál es la distancia mínima entre dos números en una computadora. Calculamos la ulp de 0, que es la distancia mínima entre 0 y el siguiente número.
Si agrega el valor calculado a 0 e intenta compararlos, no serían el mismo número. Sin embargo, si agrega un valor menor que el ulp, seguiría siendo el mismo número en lo que respecta a la computadora.
print(0 == 0 + ulp(0)) // false
print(0 == 0 + ulp(0) / 2) // true
Para nosotros es obvio que agregar un valor distinto de cero a un número producirá un número diferente, pero una computadora tiene que redondear en algún lugar, por lo que no necesariamente puede decir si dos números son iguales.
Para comparar sistemas informáticos más fácilmente, usamos el ulp de 1, y lo llamamos la máquina epsilon. Una vez que conozca la máquina epsilon, puede calcular cualquier otro ulp con la siguiente fórmula:
ulp(x) = machine epsilon * radix^exponent(x)
El valor que calculamos es muy pequeño, por lo que probablemente no alcanzará ese límite mientras codifica. Pero, calculamos el valor de 0. Con más dígitos necesarios para el lado izquierdo del punto decimal, menos tenemos para el lado derecho. Esto significa que cuanto mayor sea el número, menor precisión tendrá. En otras palabras, el ulp es una función directa del exponente. A medida que mueves el punto decimal a la derecha, el ulp aumenta y pierdes precisión.
espero que esta información le ayudará la próxima vez que reciba un extraño punto flotante de error en su código. Recuerde, las computadoras son bastante poderosas, pero incluso una computadora tiene sus límites.