Emular “doble” con 2 “flotantes”

Estoy escribiendo un progtwig para un hardware incrustado que solo admite aritmética de coma flotante de precisión simple de 32 bits. El algoritmo que estoy implementando, sin embargo, requiere una adición y comparación de doble precisión de 64 bits. Intento emular el double tipo de datos usando una tupla de dos float . Entonces una double d se emulará como una struct contiene la tupla: (float d.hi, float d.low) .

La comparación debe ser sencilla utilizando un orden lexicográfico. Sin embargo, la adición es un poco complicada porque no estoy seguro de qué base debo usar. ¿Debería ser FLT_MAX ? ¿Y cómo puedo detectar un acarreo?

¿Cómo puede hacerse esto?


Editar (Claridad): Necesito los dígitos extra significativos en lugar del rango extra.

double-float es una técnica que usa pares de números de precisión simple para lograr casi el doble de precisión que la aritmética de precisión simple acompañada de una ligera reducción del rango de exponente de precisión simple (debido a subdesbordamiento intermedio y desbordamiento en los extremos del rango) . Los algoritmos básicos fueron desarrollados por TJ Dekker y William Kahan en la década de 1970. A continuación incluyo dos artículos bastante recientes que muestran cómo estas técnicas se pueden adaptar a las GPU, sin embargo, gran parte del material cubierto en estos documentos es aplicable independientemente de la plataforma, por lo que debería ser útil para la tarea en cuestión.

http://hal.archives-ouvertes.fr/docs/00/06/33/56/PDF/float-float.pdf Guillaume Da Graça, David Defour Implementación de operadores de flotación flotante en hardware de gráficos, 7ma conferencia sobre números reales y computadoras, RNC7.

http://andrewthall.org/papers/df64_qf128.pdf Andrew Thall Números de coma flotante de precisión extendida para el cálculo de GPU.

Esto no va a ser simple.

Un flotador (IEEE 754 de precisión simple) tiene 1 bit de signo, 8 bits de exponente y 23 bits de mantisa (bueno, efectivamente 24).

Un doble (doble precisión IEEE 754) tiene 1 bit de signo, 11 bits de exponente y 52 bits de mantisa (efectivamente 53).

Puede usar el bit de signo y 8 bits de exponente de uno de sus flotantes, pero ¿cómo va a obtener 3 bits de exponente más y 29 mantitas de la otra?

Tal vez alguien más pueda proponer algo inteligente, pero mi respuesta es “esto es imposible”. (O al menos, “no es más fácil que usar una estructura de 64 bits e implementar sus propias operaciones”)

Depende un poco de los tipos de operaciones que desea realizar. Si solo te importan las adiciones y las restas, Kahan Summation puede ser una gran solución.

Si necesita tanto la precisión como un amplio rango, necesitará una implementación de software de coma flotante de doble precisión, como SoftFloat .

(Además, el principio básico es dividir la representación (por ejemplo, 64 bits) de cada valor en sus tres partes integrantes: signo, exponente y mantisa, luego cambiar la mantisa de una parte en función de la diferencia en los exponentes, agregar o restar de la mantisa de la otra parte en función de los bits del signo, y posiblemente renormalizar el resultado desplazando la mantisa y ajustando el exponente de manera correspondiente. En el camino, hay muchos detalles complicados para explicar, a fin de evitar pérdidas innecesarias de precisión, y tratar con valores especiales como infinitos, NaN y números denormalizados.)

Eso no es práctico. Si lo fuera, cada procesador de 32 bits (o comstackdor) embebido emularía la doble precisión al hacerlo. Tal como está, ninguno lo hace de lo que estoy enterado. La mayoría de ellos simplemente sustituyen el flotador por el doble.

Si necesita la precisión y no el rango dynamic, su mejor opción sería usar un punto fijo. SI el comstackdor admite 64 bits, esto también será más fácil.

Teniendo en cuenta todas las limitaciones de alta precisión sobre 23 magnitudes, creo que el método más fructífero sería implementar un paquete aritmético personalizado.

Una encuesta rápida muestra que la biblioteca doblemente duplicada de C ++ de Briggs debe abordar sus necesidades y algo más. Vea esto . [*] La implementación predeterminada se basa en el double para lograr un cálculo de 30 cifras significativas, pero se reescribe fácilmente para usar float para lograr 13 o 14 cifras significativas. Eso puede ser suficiente para sus requerimientos si se tiene cuidado de segregar operaciones de sum con valores de magnitud similares, solo sumndo extremos en las últimas operaciones.

Sin embargo, ten cuidado, los comentarios mencionan jugar con el registro de control x87. No revisé los detalles, pero eso podría hacer que el código no sea portátil para su uso.


[*] La fuente de C ++ está vinculada por ese artículo, pero solo el archivo comprimido gzip no era un vínculo inactivo.

Otra solución basada en software que podría ser de utilidad: GNU MPFR
Se ocupa de muchos otros casos especiales y permite una precisión arbitraria (mejor que el doble de 64 bits) de la que tendrías que cuidarte a ti mismo.

Esto es similar a la aritmética doble-doble utilizada por muchos comstackdores para el long double en algunas máquinas que solo tiene soporte de cálculo double hardware. También se usa como float-float en GPU NVIDIA anteriores donde no hay soporte double . De esta forma, el cálculo será mucho más rápido que una biblioteca de punto flotante de software.

Sin embargo, en la mayoría de los microcontroladores no hay soporte de hardware para float , por lo que solo se implementan en el software. Debido a eso, usar float-float puede no boost el rendimiento e introducir algo de sobrecarga de memoria para guardar los bytes adicionales de exponente.

Si realmente necesita la mantisa larga, intente utilizar una biblioteca personalizada de coma flotante. Puedes elegir lo que sea suficiente para ti, por ejemplo cambiar la biblioteca para adaptar un nuevo tipo de flotador de 48 bits si solo se necesitan 40 bits de mantisa y 7 bits de exponente. Ya no es necesario gastar tiempo para calcular / almacenar los 16 bits innecesarios. Pero esta biblioteca debe ser muy eficiente porque las bibliotecas del comstackdor a menudo tienen optimización de nivel de ensamblaje para su propio tipo de flotante.