¿Por qué las computadoras no almacenan números decimales como un segundo número entero?

24

Las computadoras tienen problemas para almacenar números fraccionarios donde el denominador no es una solución para 2 ^ x. Esto se debe a que el primer dígito después del decimal vale 1/2, el segundo 1/4 (o 1 / (2 ^ 1) y 1 / (2 ^ 2)), etc.

¿Por qué lidiar con todo tipo de errores de redondeo cuando la computadora podría haber almacenado la parte decimal del número como otro número entero (que por lo tanto es correcto?)

Lo único que se me ocurre es tratar con decimales repetidos (en la base 10), pero podría haber una solución de borde para eso (como lo tenemos actualmente con el infinito).

Algunos gatitos
fuente
8
Debe buscar cómo se almacenan los tipos decimales, en contraste con los tipos flotantes / dobles.
Oded
99
No sé cómo eso es más preciso. El primer dígito después del decimal es 1/10, el segundo 1/100, etc. ¿Cómo es eso más exacto si todavía tiene problemas de redondeo (cómo representa 1/3)? La única diferencia es qué valores se pueden representar exactamente.
Martin York
17
El punto flotante decimal (que es a lo que se refiere dos, solo en una representación más incómoda) no es más inexacto que el punto flotante binario. La única diferencia es qué valores no se pueden representar y, como estamos acostumbrados al sistema decimal, no notamos los errores de la versión decimal. Y no, ninguno puede representar todos los números racionales e irracionales.
1
Al final del día, se reduce a la eficiencia. Las computadoras son binarias y los circuitos para trabajar con esta representación binaria son mucho menos complejos. La importancia de esto puede verse algo disminuida hoy, pero hubo un momento en que esto fue muy significativo. Además, cualquier representación que elija para almacenar su número (en un espacio finito) en una computadora tendrá un conjunto finito de valores que puede representar y todos ellos exhibirán errores de redondeo con algunas entradas. El formato típico de coma flotante con Mantissa y Exponent ofrece un rango mucho mayor que el que sería posible usando dos enteros.
Mr.Mindor
1
Recomiendo leer algunos de los artículos mencionados en mi respuesta a la pregunta ¿Qué causa los errores de redondeo de punto flotante? que acabo de actualizar con detalles del último artículo de la serie referenciada. En particular, eche un vistazo a Por qué el punto fijo no curará sus puntos azules flotantes .
Mark Booth

Respuestas:

35

En realidad, hay modos de números que hacen eso.

La aritmética decimal con código binario (BCD) hace que la computadora funcione en la base 10. La razón por la que te encuentras con esto rara vez es que desperdicia espacio: cada dígito individual de un número toma un mínimo de cuatro bits, mientras que una computadora podría almacenar hasta 16 valores en ese espacio. (También puede ser más lento, pero es posible tener matemática BCD acelerada por hardware que funcione bien). De hecho, esto es exactamente lo que hacen la mayoría de las calculadoras, razón por la cual hay ciertas clases de problemas de redondeo que nunca alcanzará en un Casio de $ 5 que comerá su almuerzo en una computadora de escritorio.

La otra ruta que puede tomar es usar números racionales, es decir, un numerador y un denominador, almacenados como enteros. Esto está realmente disponible en casi todos los idiomas, es exacto y le permite almacenar todo en formatos binarios nativos. El problema es que, al final del día, los usuarios probablemente no quieran ver fracciones como 463/13, ni siquiera 35 y 8/13. Quieren ver 35.615 ..., y en el momento en que llegas allí, te enfrentas a todos los problemas típicos. Agregue que este formato ocupa aún más espacio y puede ser significativamente más lento que la aritmética de coma flotante, y no encontrará computadoras que usen este formato de manera predeterminada.

Entonces: las computadoras pueden hacer lo que quieras, pero es lento y desperdicia espacio, por lo que solo lo hacen cuando realmente tienen que hacerlo. El resto del tiempo, la velocidad y el ahorro de espacio del punto flotante son una mejor compensación.

Benjamin Pollack
fuente
¿No te refieres a cuatro bits (no bytes) en el párrafo BCD?
3
La otra opción es la aritmética de punto fijo, donde un número entero representa una fracción decimal si es un número, por ejemplo, almacenar valores monetarios (sin cálculos que involucren decimales o porcentajes) donde 1 representa $ 0.01.
Mattnz
1
@mattnz: Verdadero: los puntos fijos son un caso especial de racionales.
Jon Purdy
Impresionante, no sabía que las calculadoras hicieron eso.
SomeKittens
3
Hay una tercera opción. Punto flotante con un exponente decimal, como cómo decimalse implementa C # : stackoverflow.com/a/5019178/174335 No es BCD ya que no hay una representación individual de dígitos decimales, y no es un punto fijo.
Joren
38

Existen numerosas formas de almacenar números fraccionarios, y cada uno de ellos tiene ventajas y desventajas.

El punto flotante es, con diferencia, el formato más popular. Funciona codificando un signo, una mantisa y un exponente de base 2 con signo en números enteros, y empaquetándolos en un montón de bits. Por ejemplo, podría tener una mantisa de 32 bits de 0.5(codificada como 0x88888888) y un exponente con signo de 32 bits de +3( 0x00000003), que decodificaría a 4.0(0.5 * 2 ^ 3) Los números de coma flotante son rápidos, ya que se implementan en hardware y su precisión se escala con un tamaño absoluto, es decir, cuanto menor es el número, mejor es su precisión absoluta, por lo que el error de redondeo relativo permanece constante con el tamaño absoluto. Los flotadores son excelentes para valores muestreados de un dominio continuo, como longitudes, niveles de presión de sonido, niveles de luz, etc., y por eso, se usan comúnmente en el procesamiento de audio e imagen, así como en análisis estadísticos y simulaciones físicas. Su mayor inconveniente es que no son exactos, es decir, son propensos a errores de redondeo y no pueden representar con precisión todas las fracciones decimales. Todos los lenguajes de programación convencionales tienen un punto flotante de algún tipo.

Punto fijofunciona utilizando enteros suficientemente grandes y reservando implícitamente una parte de sus bits para la parte fraccional. Por ejemplo, un número de punto fijo de 24.8 bits reserva 24 bits para la parte entera (incluido el signo) y 8 bits para la parte fraccionaria. Desplazar a la derecha ese número en 8 bits nos da la parte entera. Los números de punto fijo solían ser populares cuando las unidades de punto flotante de hardware eran poco comunes o al menos mucho más lentas que sus homólogos enteros. Si bien los números de punto fijo son algo más fáciles de manejar en términos de exactitud (aunque solo sea porque son más fáciles de razonar), son inferiores a los flotantes en casi todos los demás aspectos: tienen menos precisión, un rango más pequeño y porque son más las operaciones son necesarias para corregir los cálculos del cambio implícito, las matemáticas de punto fijo hoy en día son a menudo más lentas que las matemáticas de punto flotante.

Los tipos decimales funcionan de manera muy similar a los números flotantes o de punto fijo, pero suponen un sistema decimal, es decir, su exponente (implícito o explícito) codifica potencia de 10, no potencia de 2. Un número decimal podría, por ejemplo, codificar una mantisa de 23456y un exponente de -2, y esto se expandiría a234.56. Los decimales, debido a que la aritmética no está conectada a la CPU, son más lentos que los flotantes, pero son ideales para cualquier cosa que implique números decimales y necesite que esos números sean exactos, con redondeos en puntos bien definidos: cálculos financieros, marcadores, etc. Algunos lenguajes de programación tienen tipos decimales incorporados (por ejemplo, C #), otros requieren bibliotecas para implementarlos. Tenga en cuenta que, si bien los decimales pueden representar con precisión fracciones decimales no repetidas, su precisión no es mejor que la de los números de coma flotante; elegir decimales simplemente significa que obtienes representaciones exactas de números que se pueden representar exactamente en un sistema decimal (al igual que los flotadores pueden representar exactamente fracciones binarias).

Los números racionales almacenan un numerador y un denumerador, generalmente usando algún tipo de tipo entero bignum (un tipo numérico que puede crecer tanto como lo permitan las restricciones de memoria de la computadora). Este es el único tipo de datos del grupo que puede modelar con precisión números como 1/3o 3/17, así como operaciones en ellos: los racionales, a diferencia de los otros tipos de datos, producirán resultados correctos para cosas como3 * 1/3. La matemática es bastante sencilla, aunque encontrar un algoritmo de factorización eficiente es bastante desafiante. Algunos lenguajes de programación tienen tipos racionales incorporados (por ejemplo, Common Lisp). Los inconvenientes de los racionales incluyen que son lentos (muchas operaciones requieren reducir fracciones y factorizar sus componentes), y que muchas operaciones comunes son difíciles o imposibles de implementar, y la mayoría de las implementaciones degradarán lo racional a flotante cuando esto suceda (por ejemplo, cuando llama sin()en un racional).

BCD (decimal codificado en binario) utiliza "nibbles" (grupos de 4 bits) para codificar dígitos individuales; Como un mordisco puede contener 16 valores diferentes, pero los números decimales requieren solo 10, hay 6 valores "ilegales" por mordisco. Al igual que los decimales, los números BCD son decimales exactos, es decir, los cálculos realizados en números decimales funcionan como lo harían si los hiciera con lápiz y papel. Las reglas aritméticas para BCD son algo torpes, pero la ventaja es que convertirlas en cadenas es más fácil que con algunos de los otros formatos, lo que es especialmente interesante para entornos de bajos recursos como los sistemas integrados.

Las cadenas , sí, cadenas antiguas simples, también se pueden usar para representar números fraccionarios. Técnicamente, esto es muy similar a BCD, solo que hay un punto decimal explícito y usa un byte completo por dígito decimal. Como tal, el formato es un desperdicio (solo se usan 11 de 256 valores posibles), pero es más fácil de analizar y generar que BCD. Además, debido a que todos los valores utilizados son "insospechados", inofensivos y neutrales a la plataforma, los números codificados en cadena pueden viajar a través de las redes sin problemas. Es poco común encontrar que la aritmética se realiza en cadenas directamente, pero es posible, y cuando lo hace, son tan exactos como los otros formatos decimales (decimales y BCD).

tdammers
fuente
Seguramente, el punto fijo de 32 bits tiene más precisión que el punto flotante de 32 bits, ya que las representaciones de punto fijo no incluyen una mantisa.
Han
44
@han: depende del tamaño del número que desea almacenar. Los flotadores le darán (aproximadamente) la misma precisión sin importar cuán grande o pequeño sea el número, mientras que el punto fijo solo le dará precisión completa si el número que desea almacenar se ajusta perfectamente a su rango.
Leo
@han No necesariamente, ambos todavía pueden representar 2 ^ 32 valores distintos. La cantidad de información transportada es idéntica, independientemente de la presentación. Sin embargo, el rango y la precisión van de la mano, por lo que, en ese sentido, la aritmética de punto fijo puede ser más precisa en ciertos rangos. Y evita desagradables problemas de redondeo aleatorio, si conoce los límites dentro de los cuales puede trabajar.
zxcdw
@han: tienen la misma precisión (o casi). La diferencia es que para los números de punto fijo, la precisión (como en el tamaño de un paso discreto de un número a su sucesor) es constante, al igual que con los enteros, mientras que con los flotadores, crece de forma lineal con un valor absoluto: el flotador el número 1.0 tiene más precisión que el número 10,000,000.0 (un millón de veces más, aproximadamente).
tdammers
6

Los números de punto flotante representan una amplia gama de valores, lo cual es muy útil cuando no sabe con anticipación cuáles podrían ser los valores, pero es un compromiso. Representar 1/10 ^ 100 con un segundo entero no funcionaría.

Algunos idiomas (y algunas bibliotecas) tienen otras características. Lisp tradicionalmente tiene enteros de precisión infinita. Cobol tiene cálculos con números decimales de punto fijo.

Debe seleccionar la representación de su número apropiado para el dominio del problema.

ddyer
fuente
1

Parece que estás describiendo números de punto fijo .

Tenga en cuenta que almacenar la parte fraccionaria de un número en una ubicación separada es exactamente idéntico a crear un solo espacio, el doble de largo, y almacenar la parte entera y fraccionaria en las dos mitades separadas. En otras palabras, es idéntico a almacenar el número como un entero pero simplemente suponiendo un número fijo de espacios decimales.

Normalmente, los números de coma flotante se almacenan usando una variación binaria en notación científica porque lo que generalmente importa son dígitos significativos. Sin embargo, existen muchos otros métodos. Los números decimales de punto fijo se usan comúnmente, por ejemplo, para almacenar valores de moneda, donde la precisión es crítica hasta cierto número entero de lugares decimales, pero el número de dígitos decimales requeridos nunca cambia.

tylerl
fuente
1

Eso se llamaría BCD, creo que todavía puedes usarlo si realmente quieres. Sin embargo, realmente no vale la pena ya que:

  1. Muy rara vez se encontrará con un error de redondeo con punto flotante de 64 bits
  2. Hace que el aritmático sea complejo e ineficiente.
  3. Pierde 6 valores cada 4 bits.
Llama invertida
fuente
Las matemáticas BCD se utilizaron mucho en los primeros sistemas de microprocesadores de 8 bits; de hecho, en un microprocesador popular (el 6502), la suma y la resta con BCD son tan rápidas por byte como con binario. Los videojuegos utilizan con frecuencia las matemáticas BCD para mantener la puntuación. No hay un manejo especial para el ajuste de puntajes en 1,000,000 de puntos. En cambio, agregar 1 a "99 99 99" produce "00 00 00" con un carry que se ignora. La sobrecarga adicional de agregar puntajes en BCD es pequeña en comparación con el costo de convertir un valor binario a formato visualizable.
supercat
1

La respuesta corta es que el punto flotante fue diseñado para cálculos científicos. Puede almacenar un número con (hasta) un número específico de dígitos significativos, lo que se ajusta estrechamente a cómo se mide la precisión en la mayoría de los cálculos científicos.

Eso tiende a ser compatible con el hardware en gran medida porque los cálculos científicos han tendido a ser los que más se han beneficiado del soporte de hardware. Por ejemplo, los cálculos financieros a menudo se realizan con otros formatos, pero el software financiero generalmente hace un cálculo real poco suficiente que, aunque los formatos necesarios solo son compatibles con el software, el rendimiento sigue siendo perfectamente adecuado para la mayoría del software financiero.

Jerry Coffin
fuente