Leyendo el siglo XXI C Llegué al capítulo 6 en la sección "Marcado de valores numéricos excepcionales con NaN" , donde explica el uso de los bits en la mantisa para almacenar algunos patrones de bits arbitrarios, para usarlos como marcadores o punteros (el libro menciona que WebKit usa esta técnica).
No estoy realmente seguro de haber entendido la utilidad de esta técnica, que veo como un truco (se basa en que el hardware no se preocupa por el valor de la mantisa en un NaN) sino que proviene de un fondo de Java al que no estoy acostumbrado la aspereza de C.
Aquí está el fragmento de código que establece y lee un marcador en un NaN
#include <stdio.h>
#include <math.h> //isnan
double ref;
double set_na(){
if (!ref) {
ref=0/0.;
char *cr = (char *)(&ref);
cr[2]='a';
}
return ref;
}
int is_na(double in){
if (!ref) return 0; //set_na was never called==>no NAs yet.
char *cc = (char *)(&in);
char *cr = (char *)(&ref);
for (int i=0; i< sizeof(double); i++)
if (cc[i] != cr[i]) return 0;
return 1;
}
int main(){
double x = set_na();
double y = x;
printf("Is x=set_na() NA? %i\n", is_na(x));
printf("Is x=set_na() NAN? %i\n", isnan(x));
printf("Is y=x NA? %i\n", is_na(y));
printf("Is 0/0 NA? %i\n", is_na(0/0.));
printf("Is 8 NA? %i\n", is_na(8));
}
imprime:
Is x=set_na() NA? 1
Is x=set_na() NAN? 1
Is y=x NA? 1
Is 0/0 NA? 0
Is 8 NA? 0
y en JSValue.h webkit explica la codificación, pero no por qué se usa.
¿Cuál es el propósito de esta técnica? ¿Son los beneficios del espacio / rendimiento lo suficientemente altos como para equilibrar su naturaleza agresiva?
fuente
Respuestas:
Cuando implementa un lenguaje de tipo dinámico, debe tener un solo tipo que pueda contener cualquiera de sus objetos. Hay tres enfoques diferentes que conozco para esto:
En primer lugar, puede pasar punteros. Esto es lo que hace la implementación de CPython. Todo objeto es un
PyObject
puntero. Estos punteros se pasan y las operaciones se realizan mirando los detalles en la estructura PyObject para descubrir el tipo.La desventaja es que los valores pequeños como los números se almacenan como valores encuadrados, por lo que sus 5 pequeños se almacenan como un bloque de memoria en alguna parte. Entonces esto nos lleva al enfoque sindical, que es utilizado por Lua. En lugar de a
PyObject*
, cada valor es una estructura de un campo para especificar el tipo, y luego una unión de todos los diferentes tipos admitidos. De esta forma, evitamos asignar memoria a valores pequeños, en lugar de almacenarlos directamente en la unión.El
NaN
enfoque almacena todo como dobles y reutiliza la parte no utilizadaNaN
para el almacenamiento adicional. La ventaja sobre el método de unión es que guardamos el campo de tipo. Si es un doble válido, es un doble, de lo contrario, la mantisa es un puntero al objeto real.Recuerde, este es cada objeto javascript. Cada variable, cada valor en un objeto, cada expresión. Si podemos reducir todos esos de 96 bits a 64 bits, eso es bastante impresionante.
¿Vale la pena el hack? Recuerde que hay mucha demanda de Javascript eficiente. Javascript es el cuello de botella en muchas aplicaciones web, por lo que hacerlo más rápido es una prioridad más alta. Es razonable introducir un cierto grado de piratería por motivos de rendimiento. Para la mayoría de los casos, sería una mala idea, ya que introduce un grado de complejidad con poca ganancia. Pero en este caso específico, vale la pena mejorar la memoria y la velocidad.
fuente
SmallInteger
.Usar NaN para "valores excepcionales" es una técnica bien conocida y a veces útil para evitar la necesidad de una variable booleana adicional
this_value_is_invalid
. Utilizado con prudencia, puede ayudarlo a hacer que su código sea más conciso, más limpio, más simple, mejor legible sin ninguna compensación de rendimiento.Esta técnica tiene algunas dificultades, por supuesto (ver aquí http://ppkwok.blogspot.co.uk/2012/11/java-cafe-1-never-write-nan-nan_24.html ), pero en lenguajes como Java ( o C # muy similar) hay funciones de biblioteca estándar como
Float.isNaN
hacer que tratar con NaNs sea simple. Por supuesto, en Java se puede utilizar como alternativa elFloat
yDouble
clase y en C # del anulable tipos de valorfloat?
ydouble?
, dándole la posibilidad de utilizarnull
en lugar de NaN para los números de punto flotante no válidas, pero esas técnicas pueden tener una influencia negativa significativa en el rendimiento y la memoria uso de su programa.En C, el uso de NaN no es 100% portátil, eso es cierto, pero puede usarlo en cualquier lugar donde esté disponible el estándar de punto flotante IEEE 754. AFAIK esto es casi todo el hardware convencional actual (o al menos el entorno de ejecución de la mayoría de los compiladores lo admite). Por ejemplo, esta publicación SO contiene información para obtener más detalles sobre el uso de NaN en C.
fuente