Как мне работать с двойниками на битовом уровне в стандарте C89?

StackOverflow https://stackoverflow.com//questions/20012843

Вопрос

Я играю с реализацией Нанесение меток NaN в небольшой языковой реализации, которую я пишу на C.Чтобы сделать это, мне нужно взять double и ткнуть непосредственно в его биты.

Сейчас у меня это работает с использованием 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)))

Но приведение к типу объединения не является стандартом ANSI C89.Есть ли надежный способ сделать это, который:

  1. Является -std=c89 -pedantic чистый?
  2. Не противоречит строгим правилам наложения псевдонимов?
  3. Может использоваться в контексте выражения, подобном этому:

    Value value = ...
    printf("The number value is %f\n", AS_NUM(value));
    
Это было полезно?

Решение

Вот краткое доказательство концепции, которое компилируется чисто и, похоже, работает корректно для меня.Я использую memcpy чтобы смягчить проблему с каламбуром.Это, конечно, может быть неприемлемо в реальной системе, но это, по крайней мере, портативно.Точно так же я не знаю, намерены ли вы требовать этого AS_NUM быть реализован в виде макроса.

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

Вот расшифровка его компиляции (с помощью Clang в OS X) и запуска:

$ cc -std=c89 -pedantic blort.c
$ ./a.out
The number value is 1.000000
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top