2013-11-16 3 views
3

Я играю с реализацией NaN tagging в небольшой реализации языка я пишу в C. Для этого мне нужно взять двойные и ткнуть прямо на своих битах.Как работать с удвоениями на уровне бит в стандартном C89?

У меня он работает в настоящее время с помощью литья накидного:

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)); 
    
+2

'беззнаковое long' составляет 32 бита на многих системах. Вероятно, вы должны использовать 'uint64_t' из' stdint.h': к сожалению, это заголовок C99. – gsg

+0

Почему вы хотите использовать 1989 C? Большинство компиляторов сегодня поддерживают как минимум C 1999, а слияние через союз поддерживается в C 1999 (tC3). ** Новое программное обеспечение не должно быть написано на старых, замененных языках **, если не существуют веские причины. –

ответ

3

Вот краткое понятие доказательство правильности, который компилирует чистый и, кажется, правильно работать для меня. Я использую 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 
+1

Очень приятно. Выполнение вызова функции, а затем вызов memcpy и возврат копии результата кажутся немного безвозмездными, просто переосмысливая те же самые биты на месте, но, глядя на сборку, кажется, что компилятор все это оптимизирует. Благодаря! – munificent