Question

Je suis jongle avec la mise en œuvre de NaN marquage dans un petit langage de mise en œuvre, je suis en train d'écrire dans C.Pour ce faire, j'ai besoin de prendre une double poche et directement à ses bits.

Je travaille maintenant à l'aide de l'union casting:

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)))

Mais un casting pour un type d'union n'est pas la norme ANSI C89.Est-il un moyen fiable pour ce faire que:

  1. Est -std=c89 -pedantic nettoyer?
  2. Ne pas aller à l'encontre de la stricte aliasing règles?
  3. Peut être utilisé dans un contexte d'expression comme ceci:

    Value value = ...
    printf("The number value is %f\n", AS_NUM(value));
    
Était-ce utile?

La solution

Voici une petite preuve-de-concept qui compile propre et semble fonctionner correctement pour moi.J'utilise memcpy pour la finesse de type jeu de mot en question.Cela peut bien sûr être inacceptable dans un système réel, mais il est au moins portable.De même, je ne sais pas si vous avez l'intention d'exiger que AS_NUM être mis en œuvre comme une macro.

#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));
}

Voici une transcription de la compilation (avec Clang sur OS X) et en cours d'exécution:

$ cc -std=c89 -pedantic blort.c
$ ./a.out
The number value is 1.000000
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top