Qual è lo scopo del pugilato NaN?

44

Leggere 21st Century C Sono arrivato al capitolo 6 nella sezione " Contrassegna valori numerici eccezionali con NaNs ", dove spiega l'uso dei bit nella mantissa per memorizzare alcuni pattern di bit arbitrari, per utilizzarli come marker o puntatori (il libro indica che WebKit utilizza questa tecnica ).

Non sono sicuro di aver compreso l'utilità di questa tecnica, che vedo come un hack (si basa sull'hardware che non si cura del valore della mantissa in un NaN) ma che proviene da uno sfondo Java non utilizzato per la ruvidezza di C.

Questo è il frammento di codice che imposta e legge un marker in 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));
}

stampa:

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

e su JSValue.h webkit spiega la codifica, ma non perché è usata.

Qual è lo scopo di questa tecnica? I benefici dello spazio / prestazioni sono abbastanza alti da bilanciare la sua natura hackish?

    
posta andijcr 31.01.2013 - 11:32
fonte

2 risposte

62

Quando implementi una lingua digitata in modo dinamico, devi avere un singolo tipo che possa contenere qualsiasi oggetto. Sono a conoscenza di tre diversi approcci per questo:

In primo luogo, puoi aggirare i puntatori. Questo è ciò che fa l'implementazione CPython. Ogni oggetto è un puntatore PyObject . Questi puntatori vengono ignorati e le operazioni vengono eseguite esaminando i dettagli nella struttura PyObject per capire il tipo.

Lo svantaggio è che i valori piccoli come i numeri vengono memorizzati come valori in box, quindi il tuo piccolo 5 viene memorizzato come un blocco di memoria da qualche parte. Quindi questo ci porta all'approccio sindacale, che viene utilizzato da Lua. Invece di PyObject* , ogni valore è una struttura che un campo specifica per il tipo e quindi un'unione di tutti i diversi tipi supportati. In questo modo evitiamo di allocare memoria per valori piccoli, invece di memorizzarli direttamente nel sindacato.

L'approccio NaN memorizza tutto come doppi e riutilizza la porzione inutilizzata di NaN per lo spazio extra. Il vantaggio rispetto al metodo di unione è che salviamo il campo del tipo. Se è un doppio valido, è un doppio altrimenti la mantissa è un puntatore all'oggetto reale.

Ricorda, questo è ogni oggetto javascript. Ogni variabile, ogni valore in un oggetto, ogni espressione. Se siamo in grado di ridurre tutti quelli da 96 bit a 64 bit è piuttosto impressionante.

Vale la pena l'hack? Ricorda che c'è molta richiesta di Javascript efficiente. Javascript è il collo di bottiglia di molte applicazioni web e quindi renderlo più veloce è una priorità più alta. È ragionevole introdurre un certo grado di scelleratezza per motivi di prestazioni. Per la maggior parte dei casi, sarebbe una cattiva idea, perché introduce un certo grado di complessità con scarso guadagno. Ma in questo caso specifico, vale la pena per migliorare la memoria e la velocità.

    
risposta data 31.01.2013 - 16:36
fonte
7

L'uso di NaN per "valori eccezionali" è una tecnica ben nota e talvolta utile per evitare la necessità di una variabile booleana extra this_value_is_invalid . Usato con saggezza, può aiutare a rendere il suo codice più conciso, più pulito, più semplice, più leggibile senza compromessi in termini di prestazioni.

Questa tecnica ha alcune insidie, naturalmente (vedi qui link ), ma in linguaggi come Java (o C # molto simile) ci sono funzioni di libreria standard come Float.isNaN per rendere semplice la gestione dei NaN. Naturalmente, in Java è possibile utilizzare alternativamente la classe Float e Double e in C # i tipi di valori nullable float? e double? , offrendo la possibilità di utilizzare null invece di NaN per numeri a virgola mobile non validi , ma tali tecniche possono avere un'influenza negativa significativa sulle prestazioni e sull'utilizzo della memoria del tuo programma.

In C l'uso di NaN non è portatile al 100%, è vero, ma è possibile utilizzarlo ovunque sia disponibile lo standard floating point IEEE 754. AFAIK questo è praticamente qualsiasi hardware mainstream oggi (o almeno l'ambiente runtime della maggior parte dei compilatori lo supporta). Ad esempio, questo post SO contiene alcune informazioni per scoprire maggiori dettagli sull'uso di NaN in C.

    
risposta data 31.01.2013 - 15:54
fonte

Leggi altre domande sui tag