El sitio web de un carca
No es sitio de moda. Sólo un 'dinosaurio' jugando.
Fraccionario-exponencial matemáticas de enteros

Fraccionario-exponencial matemáticas de enteros (fx) es una técnica de programación para almacenar y calcular los números fraccionarios y exponenciales sin las inexactitudes inherentes a los números de punto flotante (Vea nota de "Inexactitudes de punto flotante" al derecho). Puede ser preciso porque todos los valores numéricos se almacenan internamente como enteros. Esta página examina el uso de la técnica fx en C.

Inexactitudes de punto flotante

2299.500 * 1837.500 = 4225331.000

¿Puede usted ver algo erróneo en esta ecuación? Pista: el producto de la multiplicación de dos números fraccionarios ambos de los cuales terminan en .5 debe también ser fraccionario, terminando con .25 o .75; el producto no puede ser un número entero. (En este caso, el producto de 2299.5 por 1837.5 es 4225331.25) La ecuación inexacta anterior fue generada por la computadora Dell vieja y confiable de mi casa. Específicamente, es la salida de este código C compilado con gcc 3.3.5:

float a, b, x;
a = 2299.5;
b = 1837.5;
x = a * b;
printf("%.3f * %.3f = %.3f\n",a,b,x);

El problema no está en el equipo ni el código ni el compilador, sino en la naturaleza de los números de punto flotante. Es cierto que se podría corregir el ejemplo específico anterior por usar el tipo double, o por usar una implementación diferente del tipo float. Pero en algún momento de algún otro tipo de cálculo, las limitaciones internas de aproximaciones de abscisas-mantisa del punto flotante resultaría en unas inexactitudes similares, o peores. ¿Quiere un ejemplo de peor? Mire esto:

0.0 ^ 0.0 = 1.0

En la lógica de números de punto flotante. de cualquier aplicación, de cualquier precisión, ¡cero elevado a la potencia de cero es igual a uno! ¿Cómo puede será esto? Debido a las aproximaciones de abscisas-mantisa, en las que números de punto flotante son raramente o nunca absolutamente precisos. Cero de punto flotante no es realmente cero, sino una aproximación cercana a cero. Bueno, se puede decir que esto es de fiar. Es matemáticamente correcto decir que una aproximación de cero elevado a la potencia de una aproximación de cero será igual a una aproximación de 1 (uno). Sin embargo, ¿se puede encontrar un matemático o ingeniero honesto que aceptaría el cálculo de punto flotante arriba? Este ingeniero digo que eso no es buena lógica.

Mi aplicación de fx utiliza estas dos estructuras C:

#define IntSize long

struct frac
    {
    IntSize num;
    IntSize den;
    };

struct fx
    {
    struct frac base;
    struct frac exp;
    } xx;

(IntSize está aquí igual de tipo long, dándole precisión de 32 o 64 bits, dependiendo de la máquina de destino. Su definición podría ser alterado para lograr una mayor precisión o más portátil, dependiendo de las necesidades del desarrollador.)

Todos los números fx tienen la forma struct fx, es decir, un número base fraccionario (struct frac) con un exponente fraccionario. Los 4 elementos son enteros IntSize. Donde el fx su mismo es un número entero, todos menos el xx.base.num son 1 (uno). Por ejemplo, el número entero 142 tendría la forma

xx.base.num = 142;
xx.base.den = 1;
xx.exp.num = 1;
xx.exp.den = 1;

Que es

(142 / 1) ^ (1 / 1)

La cantidad 142 dividido por 1, elevado a la potencia de la cantidad de 1 divido por 1

Fracciones

Un ejemplo de un valor fraccionario puede ser

(1 / 3) ^ (1 / 1)

No hay decimal repetiendo .3333 y no riesgo de errores de redondear. De veras, no hay intento de realizar la operación de división. La fracción se almacena sencillamente como una fracción de numerador / denominador, todos los elementos todavía son enteros IntSize .

Para números con punto decimal, el número fx empieza a asemejarse al concepto del punto decimal implícito de Cobol. xx.base.den es siempre algúna potencia de diez, dependiendo de cuantos decimales implícitos hay. En una applicación de negocio donde hay campos de dólares y centavos, el denominador sería 100. Por ejemplo, la cantidad de $547.95 sería

(54795 / 100) ^ (1 / 1)

Si IntSize es un número entero de 64 bits, el número fx puede representar un número decimal con 18 dígitos significativos (como en Cobol), antes o despues del punto decimal implícito. Esto se traduce en campos de dólares y centavos capaz para mil millones de millones de dólares sin nunca perder un solo centavo a errores de redondear o inexactitudes de punto flotante. Eso es (¿casi?) suficiente para llevar la cuenta de la entera deuda nacional de EEUU, hasta el último centavo. Si el desarrollador necesita más capacidad, puede definir IntSize con 128 bits, o según la capacidad de la máquina de destino.

Exponentes

La segunda mitad del número fx is el exponente, la potencia a que la fracción del base está elevado. Es también un número fraccionario, a fin de que potencias fraccionarias (raíces) pueden ser representadas. Entonces, por ejemplo, trece elevado al cubo sería

xx.base.num = 13;
xx.base.den = 1;
xx.exp.num = 3;
xx.exp.den = 1;

Que es

(13 / 1) ^ (3 / 1)

La raíz cuadrada de .7 sería

(7 / 10) ^ (1 / 2)

Así como las fracciones del base, no hay intento de realizar la operación de exponenciación. El exponente se almacena sencillamente como dos enteros IntSize.

Números Imaginarios

Una característica extraordinaria de la técnica fx es la habilidad a tratar con números imaginarios, es decir, los números basado en i, la raíz cuadrada de uno negativo.

(-1 / 1) ^ (1 / 2)

El número fx a la izquierda se podría definir con respecto a i  como el número complejo
((5^(1/2))*i)^(7/2)
pero para hacer algo con esto en C requeriría una extensión a la biblioteca matemática convencional de C, y aún así conllevaría las matemáticas de punto flotante con sus inexactitudes inherentes. (Además, yo no sabría escribir el código C.)
Usted lo aprendió en la secundaria. Cualquier número negativo con raíz par es un número imaginario. Este número fx, por ejemplo:

(-5 / 1) ^ (7 / 4)

está imposible a resolver dentro el dominio de números reales. Pero las normas de matemáticas aún aplican. Por ejemplo, se pueden multiplicar dos números imaginarios:

  (-5 / 1) ^ (7 / 4)
* (-5 / 1) ^ (5 / 4)
 -------------------
  (-5 / 1) ^ (12/ 4)

El producto, en esto caso, ya no tiene raíz par (desde se puede simplificar el exponente al entero 3), y por eso se puede resolver como el número real -125.

El punto es, la técnica fx puede hacer con facilitad el cálculo arriba y otros que consisten en números imaginarios. Yo no sabría hacerlo usando números de punto flotante u otro método convencional de programación.

Limitaciones

Como notado arriba, la capacidad máxima del número fx está limitada por la definición de su elemento IntSize.

La otra limitación es que los números trascendentales como pi, log, y funciones trig no pueden ser representados por números fx.

Implementación

Las estructuras, definiciones, y funciones C son incluidos en el archivo header de fx, el cual usted puede descargar y incluir en su propia programa C.

También espero incluir una página para exponer en detalle las varias funciones fx de este archivo para operaciones de matemáticas, etc.

   rev. 2019.07.18