floating point - Punto flotante de precisión extendida de 80 bits en OCaml
floating-point extended-precision (1)
¿Existe una biblioteca OCaml para aprovechar el tipo de punto flotante de precisión extendida de 80 bits en las arquitecturas IA-32 y x86-64?
Sé acerca de los enlaces MPFR pero mi biblioteca ideal sería más liviana. Aprovechar las instrucciones históricas de punto flotante sería ideal.
La implementación de una biblioteca de este tipo es posible fuera del compilador, gracias a la compatibilidad del lenguaje con el FFI .
La biblioteca debe dividirse en dos partes: la parte de origen de Ocaml nativa y la parte de tiempo de ejecución de C. La fuente OCaml debe contener la declaración del tipo de datos, así como la declaración de todas las funciones importadas. Por ejemplo, la operación de agregar sería:
(** basic binary operations on long doubles *)
external add : t -> t -> t = "ml_float80_add"
external sub : t -> t -> t = "ml_float80_sub"
external mul : t -> t -> t = "ml_float80_mul"
external div : t -> t -> t = "ml_float80_div"
en el código C, la función ml_float80_add
debe definirse, como se describe en el manual de OCaml:
CAMLprim value ml_float80_add(value l, value r){
float80 rlf = Float80_val(l);
float80 rrf = Float80_val(r);
float80 llf = rlf + rrf;
value res = ml_float80_copy(llf);
return res;
}
Aquí convertimos las representaciones en tiempo de ejecución de value
OCaml a valores C nativos, usamos el operador binario en ellas y devolvemos un nuevo valor de OCaml. La función ml_float80_copy
hace la asignación de esa representación en tiempo de ejecución.
Del mismo modo, las implementaciones en C de las funciones sub
, mul
y div
deben definirse allí. Uno puede notar la similitud en la firma y la implementación de estas funciones y abstraerse mediante el uso de macros C:
#define FLOAT80_BIN_OP(OPNAME,OP) /
CAMLprim value ml_float80_##OPNAME(value l, value r){ /
float80 rlf = Float80_val(l); /
float80 rrf = Float80_val(r); /
float80 llf = rlf OP rrf; /
value res = ml_float80_copy(llf); /
return res; /
}
FLOAT80_BIN_OP(add,+);
FLOAT80_BIN_OP(sub,-);
FLOAT80_BIN_OP(mul,*);
FLOAT80_BIN_OP(div,/);
El resto del módulo OCaml y C debe seguir.
Hay muchas posibilidades en cuanto a cómo codificar el tipo float80
C en un valor OCaml. La opción más sencilla es utilizar una cadena y almacenar en ella el long double
.
type t = string
En el lado C, definimos las funciones para convertir un valor OCaml de ida y vuelta a un valor C:
#include <caml/mlvalues.h>
#include <caml/alloc.h>
#include <caml/misc.h>
#include <caml/memory.h>
#define FLOAT80_SIZE 10 /* 10 bytes */
typedef long double float80;
#define Float80_val(x) *((float80 *)String_val(x))
void float80_copy_str(char *r, const char *l){
int i;
for (i=0;i<FLOAT80_SIZE;i++)
r[i] = l[i];
}
void store_float80_val(value v,float80 f){
float80_copy_str(String_val(v), (const char *)&f);
}
CAMLprim value ml_float80_copy(value r, value l){
float80_copy_str(String_val(r),String_val(l));
return Val_unit;
}
Sin embargo, esa implementación no ofrece soporte para las funciones de comparación polimórficas integradas en OCaml Pervasive.compare
, y algunas otras características. El uso de esa función en el tipo float80 anterior inducirá a error a la función de comparación a creer que los valores son cadenas, y hará una comparación lexicográfica de su contenido.
Sin embargo, soportar estas características especiales es bastante simple. Redefinimos el tipo OCaml como abstracto y cambiamos el código C para crear y manejar estructuras personalizadas para nuestro float80:
#include <caml/mlvalues.h>
#include <caml/alloc.h>
#include <caml/misc.h>
#include <caml/memory.h>
#include <caml/custom.h>
#include <caml/intext.h>
typedef struct {
struct custom_operations *ops;
float80 v;
} float80_s;
#define Float80_val(x) *((float80 *)Data_custom_val(x))
inline int comp(const float80 l, const float80 r){
return l == r ? 0: (l < r ? -1: 1);
}
static int float80_compare(value l, value r){
const float80 rlf = Float80_val(l);
const float80 rrf = Float80_val(r);
const int llf = comp(rlf,rrf);
return llf;
}
/* other features implementation here */
CAMLexport struct custom_operations float80_ops = {
"float80", custom_finalize_default, float80_compare, float80_hash,
float80_serialize, float80_deserialize, custom_compare_ext_default
};
CAMLprim value ml_float80_copy(long double ld){
value res = caml_alloc_custom(&float80_ops, FLOAT80_SIZE, 0, 1);
Float80_val(res) = ld;
return res;
}
Luego, proponemos construir todo usando ocamlbuild y un pequeño script de bash.