¿Cómo puedo trabajar con dobles en el nivel de bits en la norma C89?
-
21-12-2019 - |
Pregunta
Estoy jugando con la implementación de NaN etiquetado en un poco de lenguaje de implementación estoy escribiendo en C.Para ello, necesito tomar una doble y meter directamente en su bits.
Lo tengo funcionando ahora mediante la unión de fundición:
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)))
Pero la conversión a un tipo de unión no es estándar ANSI C89.¿Hay una forma segura de hacer esto:
- Es
-std=c89 -pedantic
limpia? - No ir en contra de alias estricto de las reglas?
Puede ser utilizado en el contexto de una expresión como esta:
Value value = ... printf("The number value is %f\n", AS_NUM(value));
Solución
Aquí hay una rápida prueba-de-concepto en el que se compila limpio y parece funcionar correctamente para mí.Yo uso memcpy
a la finura de tipo de juego de palabras en cuestión.Esto puede, por supuesto, inaceptable en un sistema real, pero al menos portátil.Del mismo modo, no sé si usted tiene la intención de exigir que AS_NUM
ser implementado como una 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));
}
He aquí una transcripción de la compilación (con Clang en OS X) y ejecutar:
$ cc -std=c89 -pedantic blort.c
$ ./a.out
The number value is 1.000000