биты с плавающей точкой и строгое наложение - PullRequest
21 голосов
/ 01 декабря 2010

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

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

Ответы [ 4 ]

16 голосов
/ 01 декабря 2010

Единственный способ по-настоящему избежать любых проблем - это 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;
}

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

При этом метод объединения ОЧЕНЬ широко поддерживается.

6 голосов
/ 02 декабря 2010

Профсоюзный хак - это определенно неопределенное поведение, верно?

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

5 голосов
/ 08 февраля 2011

Следующее не нарушает правило псевдонимов, поскольку оно не использует lvalues ​​для доступа к различным типам где-либо

0 голосов
/ 01 декабря 2010

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

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

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

...