À la lecture de 21st Century C, je suis arrivé au chapitre 6 à la section "Marquage de valeurs numériques exceptionnelles avec NaN" , où il est expliqué comment utiliser les bits de la mantisse pour stocker des motifs de bits arbitraires, pour les utiliser comme marqueurs ou pointeurs (le livre mentionne que WebKit utilise cette technique).
Je ne suis pas vraiment sûr d'avoir compris l'utilité de cette technique, que je vois comme un bidouillage (le matériel ne tient pas compte de la valeur de la mantisse dans un NaN) mais venant d'un arrière-plan Java auquel je ne suis pas habitué la rugosité de C.
Voici l'extrait de code qui définit et lit un marqueur dans 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));
}
il 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
et à JSValue.h webkit explique le codage, mais pas pourquoi il est utilisé.
Quel est le but de cette technique? Les avantages de l’espace / de la performance sont-ils suffisamment élevés pour contrebalancer sa nature rudimentaire?
la source
Réponses:
Lorsque vous implémentez un langage typé dynamiquement, vous devez avoir un seul type pouvant contenir n'importe lequel de vos objets. Je connais trois approches différentes pour cela:
Tout d'abord, vous pouvez faire passer des pointeurs. C'est ce que fait l'implémentation CPython. Chaque objet est un
PyObject
pointeur. Ces pointeurs sont distribués et les opérations sont effectuées en examinant les détails dans la structure PyObject pour déterminer le type.L'inconvénient est que les petites valeurs telles que les nombres sont stockées sous forme de valeurs encadrées. Ainsi, votre petit 5 est stocké sous forme de bloc de mémoire quelque part. Cela nous conduit donc à l’approche d’union, utilisée par Lua. Au lieu de a
PyObject*
, chaque valeur est une structure dans laquelle un champ spécifie le type, puis une union de tous les différents types pris en charge. De cette façon, nous évitons d'allouer de la mémoire pour de petites valeurs, mais de les stocker directement dans l'union.L’
NaN
approche stocke tout en double et réutilise la partie inutiliséeNaN
pour un espace de stockage supplémentaire. L'avantage par rapport à la méthode d'union est que nous sauvegardons le champ de type. Si c'est un double valide, c'est un double, sinon la mantisse est un pointeur sur l'objet réel.N'oubliez pas qu'il s'agit de tous les objets javascript. Chaque variable, chaque valeur dans un objet, chaque expression. Si nous pouvons réduire toutes ces fréquences de 96 bits à 64 bits, c'est assez impressionnant.
Vaut-il le bidouillage? Rappelez-vous qu'il existe une forte demande pour un Javascript efficace. Javascript est le goulot d'étranglement dans de nombreuses applications Web, il est donc prioritaire de l'accélérer. Il est raisonnable d'introduire un certain degré de hackiness pour des raisons de performances. Dans la plupart des cas, ce serait une mauvaise idée, car il introduit un degré de complexité pour un gain minime. Mais dans ce cas précis, cela vaut la peine d’améliorer la mémoire et la vitesse.
la source
SmallInteger
.Utiliser NaN pour des "valeurs exceptionnelles" est une technique bien connue et parfois utile pour éviter le recours à une variable booléenne supplémentaire
this_value_is_invalid
. Utilisé à bon escient, il peut aider à rendre son code plus concis, plus propre, plus simple et plus lisible sans compromis en termes de performances.Cette technique a bien sûr quelques pièges (voir ici http://ppkwok.blogspot.co.uk/2012/11/java-cafe-1-never-write-nan-nan_24.html ), mais dans des langages comme Java ( ou très similaire en C #), il existe des fonctions de bibliothèque standard
Float.isNaN
permettant de traiter facilement les NaN. Bien sûr, en Java , vous pouvez utiliser alternativement laFloat
etDouble
classe et en C # types de valeurs annulablefloat?
etdouble?
, en vous donnant la possibilité d'utiliser aunull
lieu de NaN pour les nombres à virgule flottante non valides, mais ces techniques peuvent avoir une influence négative importante sur la performance et de la mémoire utilisation de votre programme.En C, l’utilisation de NaN n’est pas portable à 100%, c’est vrai, mais vous pouvez l’utiliser partout où la norme à virgule flottante IEEE 754 est disponible. Autant que je sache, il s’agit à peu près de tous les matériels traditionnels (ou du moins l’environnement d’exécution de la plupart des compilateurs le supporte). Par exemple, cet article sur le SO contient des informations pour en savoir plus sur l'utilisation de NaN en C.
la source