Frage

Ich spiele mit der Umsetzung NaN-Tagging In einer kleinen Sprachimplementierung schreibe ich in C.Dazu muss ich einen Doppelgänger nehmen und direkt in dessen Teile stechen.

Ich habe es jetzt mit Union Casting zum Laufen gebracht:

typedef union
{
  double num;
  unsigned long bits;
} Value;

/* A mask that selects the sign bit. */
#define SIGN_BIT (1UL << 63)

/* The bits that must be set to indicate a quiet NaN. */
#define QNAN (0x7ff8000000000000L)

/* If the NaN bits are set, it's not a number. */
#define IS_NUM(value) (((value).bits & QNAN) != QNAN)

/* Convert a raw number to a Value. */
#define NUM_VAL(n) ((Value)(double)(n))

/* Convert a Value representing a number to a raw double. */
#define AS_NUM(value) (value.num)

/* Converts a pointer to an Obj to a Value. */
#define OBJ_VAL(obj) ((Value)(SIGN_BIT | QNAN | (unsigned long)(obj)))

/* Converts a Value representing an Obj pointer to a raw Obj*. */
#define AS_OBJ(value) ((Obj*)((value).bits & ~(SIGN_BIT | QNAN)))

Die Umwandlung in einen Union-Typ entspricht jedoch nicht dem ANSI C89-Standard.Gibt es eine zuverlässige Möglichkeit, dies zu tun:

  1. Ist -std=c89 -pedantic sauber?
  2. Verstößt es nicht gegen strenge Aliasing-Regeln?
  3. Kann in einem Ausdruckskontext wie diesem verwendet werden:

    Value value = ...
    printf("The number value is %f\n", AS_NUM(value));
    
War es hilfreich?

Lösung

Hier ist ein kurzer Proof-of-Concept, der sauber kompiliert wird und für mich korrekt zu laufen scheint.ich benutze memcpy um das Wortspielproblem zu verfeinern.Das mag in einem realen System natürlich inakzeptabel sein, aber es ist zumindest portabel.Ebenso weiß ich nicht, ob Sie beabsichtigen, dies zu verlangen AS_NUM als Makro implementiert werden.

#include <stdio.h>
#include <string.h>

typedef struct {
    char raw[sizeof(double)];
} Value;

static Value valueFromDouble(double d) {
    Value result;
    memcpy(result.raw, &d, sizeof(result));
    return result;
}

static double AS_NUM(Value value) {
    double result;
    memcpy(&result, value.raw, sizeof(result));
    return result;
}

int main(int argc, char **argv) {
    Value value = valueFromDouble(1.0);
    printf("The number value is %f\n", AS_NUM(value));
}

Hier ist eine Abschrift des Kompilierens (mit Clang unter OS X) und der Ausführung:

$ cc -std=c89 -pedantic blort.c
$ ./a.out
The number value is 1.000000
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top