web-dev-qa-db-fra.com

Quel est le but de la boxe NaN?

Lecture 21st Century C Je suis arrivé au chapitre 6 à la section "Marquage des valeurs numériques exceptionnelles avec NaNs" , où il explique l'utilisation des bits de la mantisse pour stocker des valeurs arbitraires modèles de bits, 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 hack (il repose sur le matériel ne se souciant pas de la valeur de la mantisse dans un NaN) mais provenant d'un arrière-plan Java I ' m 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 l'encodage, mais pas pourquoi il est utilisé.

Quel est le but de cette technique? Les avantages de l'espace/performances sont-ils suffisamment élevés pour équilibrer sa nature hackeuse?

45
andijcr

Lorsque vous implémentez un langage typé dynamiquement, vous devez avoir un seul type qui peut contenir n'importe lequel de vos objets. Je connais trois approches différentes pour cela:

Tout d'abord, vous pouvez contourner les pointeurs. C'est ce que fait l'implémentation de CPython. Chaque objet est un pointeur PyObject. Ces pointeurs sont transmis et les opérations sont effectuées en examinant les détails de la structure PyObject pour déterminer le type.

L'inconvénient est que les petites valeurs comme les nombres sont stockées sous forme de valeurs encadrées, donc votre petit 5 est stocké sous forme de bloc de mémoire quelque part. Cela nous amène donc à l'approche syndicale, utilisée par Lua. Au lieu d'un PyObject*, chaque valeur est une structure dont un champ pour spécifier 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 les petites valeurs, au lieu de les stocker directement dans l'union.

L'approche NaN stocke tout en double et réutilise la portion inutilisée de NaN pour le stockage supplémentaire. L'avantage par rapport à la méthode d'union est que nous enregistrons le champ type. Si c'est un double valide, c'est un double sinon la mantisse est un pointeur vers l'objet réel.

Rappelez-vous, c'est chaque objet javascript. Chaque variable, chaque valeur dans un objet, chaque expression. Si nous pouvons réduire tout cela de 96 bits à 64 bits, c'est assez impressionnant.

Vaut-il le coup? Rappelons qu'il existe une forte demande pour Javascript efficace. Javascript est le goulot d'étranglement dans de nombreuses applications Web, et donc le rendre plus rapide est une priorité plus élevée. Il est raisonnable d'introduire un certain degré de piratage pour des raisons de performances. Dans la plupart des cas, ce serait une mauvaise idée, car cela introduirait un degré de complexité pour peu de gain. Mais dans ce cas spécifique, cela vaut la peine d'améliorer la mémoire et la vitesse.

64
Winston Ewert

L'utilisation de NaN pour les "valeurs exceptionnelles" est une technique bien connue et parfois utile pour éviter le besoin d'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, mieux lisible sans compromis sur les 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 C # très similaire), il existe des fonctions de bibliothèque standard comme Float.isNaN pour simplifier le traitement des NaN. Bien sûr, dans Java vous pouvez utiliser alternativement les classes Float et Double et en C # les types de valeurs nullables float? et double?, vous donnant la possibilité d'utiliser null au lieu de NaN pour les nombres à virgule flottante non valides, mais ces techniques peuvent avoir une influence négative significative sur les performances et l'utilisation de la mémoire de votre programme.

En C, l'utilisation de NaN n'est pas 100% portable, c'est vrai, mais vous pouvez l'utiliser partout où la norme à virgule flottante IEEE 754 est disponible. AFAIK, il s'agit aujourd'hui de presque tous les matériels grand public (ou du moins l'environnement d'exécution de la plupart des compilateurs le prend en charge). Par exemple, this SO post contient des informations pour en savoir plus sur l'utilisation de NaN en C.

7
Doc Brown