Вопрос

Я пытаюсь извлечь биты из поплавка, не вызывая неопределенного поведения. Вот моя первая попытка:

unsigned foo(float x)
{
    unsigned* u = (unsigned*)&x;
    return *u;
}

Насколько я понимаю, это не гарантированно будет работать из -за строгих правил псевдонимы, верно? Это работает, если сделать промежуточный шаг с указателем персонажа?

unsigned bar(float x)
{
    char* c = (char*)&x;
    unsigned* u = (unsigned*)c;
    return *u;
}

Или мне нужно извлечь индивидуальные байты сам?

unsigned baz(float x)
{
    unsigned char* c = (unsigned char*)&x;
    return c[0] | c[1] << 8 | c[2] << 16 | c[3] << 24;
}

Конечно, это имеет недостаток в зависимости от эндсианности, но я мог бы жить с этим.

Хак Юнион определенно неопределенный поведение, верно?

unsigned uni(float x)
{
    union { float f; unsigned u; };
    f = x;
    return u;
}

Просто для полноты, вот справочная версия foo. Анкет Также неопределенное поведение, верно?

unsigned ref(float x)
{
    return (unsigned&)x;
}

Итак, возможно ли извлечь биты из поплавка (Предполагая, что оба имеют ширину 32 бита, конечно)?


РЕДАКТИРОВАТЬ: И вот memcpy Версия, предложенная Гоз. Поскольку многие компиляторы не поддерживают static_assert Тем не менее, я заменил static_assert С некоторым шаблоном метапрограммирования:

template <bool, typename T>
struct requirement;

template <typename T>
struct requirement<true, T>
{
    typedef T type;
};

unsigned bits(float x)
{
    requirement<sizeof(unsigned)==sizeof(float), unsigned>::type u;
    memcpy(&u, &x, sizeof u);
    return u;
}
Это было полезно?

Решение

О единственном способе по -настоящему избежать любых проблем - это Memcpy.

unsigned int FloatToInt( float f )
{
   static_assert( sizeof( float ) == sizeof( unsigned int ), "Sizes must match" );
   unsigned int ret;
   memcpy( &ret, &f, sizeof( float ) );
   return ret;
}

Поскольку вы Memcpying фиксированная сумма, компилятор оптимизирует его.

Тем не менее, метод профсоюза очень широко поддерживается.

Другие советы

Хак Юнион определенно неопределенный поведение, верно?

Да и нет. Согласно стандарту, это определенно неопределенное поведение. Но это такой широко используемый трюк, что GCC и MSVC и, насколько я знаю, любой другой популярный компилятор, явно гарантирует, что он безопасен и будет работать так, как ожидалось.

Следующее не нарушает правило псевдонизации, потому что оно не использует LValues, получающие доступ к разным типам.

template<typename B, typename A>
B noalias_cast(A a) { 
  union N { 
    A a; 
    B b; 
    N(A a):a(a) { }
  };
  return N(a).b;
}

unsigned bar(float x) {
  return noalias_cast<unsigned>(x);
}

Если вы действительно хотите быть агностическими по поводу размера типа поплавка и просто верните необработанные биты, сделайте что -то вроде этого:

void float_to_bytes(char *buffer, float f) {
    union {
        float x;
        char b[sizeof(float)];
    };

    x = f;
    memcpy(buffer, b, sizeof(float));
}

Тогда назовите это так:

float a = 12345.6789;
char buffer[sizeof(float)];

float_to_bytes(buffer, a);

Этот метод, конечно, будет производить выход, специфичный для заказа байта вашей машины.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top