C 使用最重要的位标记工会是否被认为是一种不好的做法?

C 使用最重要的位标记工会是否被认为是一种不好的做法?,c,performance,design-patterns,optimization,unions,C,Performance,Design Patterns,Optimization,Unions,假设我有以下标记的联合: // f32 is a float of 32 bits // uint32 is an unsigned int of 32 bits struct f32_or_uint32 { char tag; union { f32 f; uint32 u; } } 如果标记==0,则它是一个f32。如果标记==1,则它是uint32。这种表示法只有一个问题:它使用64位,而实际上只需要33位。这几乎是一种“1/2”的

假设我有以下标记的联合:

// f32 is a float of 32 bits
// uint32 is an unsigned int of 32 bits
struct f32_or_uint32 {
    char tag;
    union {
        f32 f;
        uint32 u;
    }
}
如果
标记==0
,则它是一个f32。如果
标记==1
,则它是
uint32
。这种表示法只有一个问题:它使用64位,而实际上只需要33位。这几乎是一种“1/2”的浪费,当您处理巨大的缓冲区时,这可能是相当大的浪费。我从不使用32位,所以我想用一位作为标志,这样做:

#define IS_UINT32(x) (!(x&0x80000000))
#define IS_F323(x) (x&0x80000000)
#define MAKE_F32(x) (x|0x80000000)
#define EXTRACT_F32(x) (x&0x7FFFFFF)
union f32_or_uint32 {
    f32 f;
    uint32 u;
}

这样,我使用31位作为值,仅1位作为标记。我的问题是:这种做法会对性能、可维护性和可移植性有害吗?

不,你不能这样做。至少,不是一般意义上的

无符号整数具有2^32个不同的值。它使用所有32位。同样,浮点值也有(几乎)2^32个不同的值。它使用所有32位

在您实际想要使用的值范围内,如果小心一点,很可能会在一种类型中隔离一个始终为1、另一种类型中始终为0的位。如果您决定只使用不超过2^31的值,则可以使用unsigned int的高位。如果您不介意一个小的舍入误差,则可以使用较低的浮点值

如果无符号整数的范围较小(比如仅23位),则有更好的策略可用。您可以选择1+8位的高阶位模式,这对于您的浮点使用是非法的。也许没有+/-无限你也能应付?请尝试0x1ff


为了回答你的其他问题,在C++中使用类和一些内联函数创建一个新的类型,并获得良好的性能是比较容易的。用C语言编写宏会更容易侵入代码,更容易出现错误,但性能类似。在大多数正常使用中,不太可能检测到执行这些测试以及可能执行某些掩码操作所需的指令开销。显然,在计算密集型使用的情况下,必须重新考虑这一点,但您可以将其视为典型的空间/速度权衡。

不,您不能这样做。至少,不是一般意义上的

无符号整数具有2^32个不同的值。它使用所有32位。同样,浮点值也有(几乎)2^32个不同的值。它使用所有32位

在您实际想要使用的值范围内,如果小心一点,很可能会在一种类型中隔离一个始终为1、另一种类型中始终为0的位。如果您决定只使用不超过2^31的值,则可以使用unsigned int的高位。如果您不介意一个小的舍入误差,则可以使用较低的浮点值

如果无符号整数的范围较小(比如仅23位),则有更好的策略可用。您可以选择1+8位的高阶位模式,这对于您的浮点使用是非法的。也许没有+/-无限你也能应付?请尝试0x1ff


为了回答你的其他问题,在C++中使用类和一些内联函数创建一个新的类型,并获得良好的性能是比较容易的。用C语言编写宏会更容易侵入代码,更容易出现错误,但性能类似。在大多数正常使用中,不太可能检测到执行这些测试以及可能执行某些掩码操作所需的指令开销。显然,在计算密集型使用的情况下,必须重新考虑这一点,但您可以将其视为典型的空间/速度权衡。

不,您不能这样做。至少,不是一般意义上的

无符号整数具有2^32个不同的值。它使用所有32位。同样,浮点值也有(几乎)2^32个不同的值。它使用所有32位

在您实际想要使用的值范围内,如果小心一点,很可能会在一种类型中隔离一个始终为1、另一种类型中始终为0的位。如果您决定只使用不超过2^31的值,则可以使用unsigned int的高位。如果您不介意一个小的舍入误差,则可以使用较低的浮点值

如果无符号整数的范围较小(比如仅23位),则有更好的策略可用。您可以选择1+8位的高阶位模式,这对于您的浮点使用是非法的。也许没有+/-无限你也能应付?请尝试0x1ff


为了回答你的其他问题,在C++中使用类和一些内联函数创建一个新的类型,并获得良好的性能是比较容易的。用C语言编写宏会更容易侵入代码,更容易出现错误,但性能类似。在大多数正常使用中,不太可能检测到执行这些测试以及可能执行某些掩码操作所需的指令开销。显然,在计算密集型使用的情况下,必须重新考虑这一点,但您可以将其视为典型的空间/速度权衡。

不,您不能这样做。至少,不是一般意义上的

无符号整数具有2^32个不同的值。它使用所有32位。同样,浮点值也有(几乎)2^32个不同的值。它使用所有32位

在您实际想要使用的值范围内,如果小心一点,很可能会在一种类型中隔离一个始终为1、另一种类型中始终为0的位。如果您决定只使用不超过2^31的值,则可以使用unsigned int的高位。如果您不介意一个小的舍入误差,则可以使用较低的浮点值

如果无符号整数的范围较小(比如仅23位),则有更好的策略可用。您可以选择1+8位的高阶位模式,这对于您的浮点使用是非法的。也许没有+/-无限你也能应付?请尝试0x1ff

为了回答你的其他问题,在C++中使用类和一些内联函数创建一个新的类型,并获得良好的性能是比较容易的。用C语言中的宏实现
#include <stdio.h>

typedef union tag_uifp {
    unsigned int ui32;
    float fp32;
} uifp;

#define FLOAT_VALUE 0x00
#define UINT_VALUE  0x01

int get_type(uifp x) {
    return x.ui32 & 1;
}

unsigned get_uiv(uifp x) {
    return x.ui32 >> 1;
}

float get_fpv(uifp x) {
    return x.fp32;
}

uifp make_uiv(unsigned x) {
    uifp result;
    result.ui32 = 1 + (x << 1);
    return result;
}

uifp make_fpv(float x) {
    uifp result;
    result.fp32 = x;
    result.ui32 &= ~1;
    return result;
}

uifp data[10];

void setNumbers() {
    int i;
    for (i=0; i<10; i++) {
        data[i] = (i & 1) ? make_fpv(i/10.0) : make_uiv(i);
    }
}

void printNumbers() {
    int i;
    for (i=0; i<10; i++) {
        if (get_type(data[i]) == FLOAT_VALUE) {
            printf("%0.3f\n", get_fpv(data[i]));
        } else {
            printf("%i\n", get_uiv(data[i]));
        }
        data[i] = (i & 1) ? make_fpv(i) : make_uiv(i);
    }
}

int main(int argc, const char *argv[]) {
    setNumbers();
    printNumbers();
    return 0;
}
typedef uint32_t tagged_t;

tagged_t float_to_tagged(float f) {
  uint32_t ret;
  memcpy(&ret, &f, sizeof(f));
  // Make sure the user didn't pass us a negative number.
  assert((ret & 0x80000000) == 0);
  return ret | 0x80000000
}
float tagged_to_float(tagged_t val) {
  float ret;
  val &= 0x7FFFFFF;
  memcpy(&ret, &val, sizeof(val));
  return ret;
}