Порядок следования битов в структуре не тот, который я ожидал - PullRequest
4 голосов
/ 04 июня 2019

У меня есть фреймворк, который использует 16-битные числа с плавающей запятой, и я хотел разделить его компоненты, чтобы затем использовать их для 32-разрядных операций с плавающей запятой. В моем первом подходе я использовал сдвиги битов и тому подобное, и хотя это работало, читать было дико хаотично.

Затем я хотел использовать вместо этого структуры с пользовательским размером в битах и ​​использовать объединение для записи в эту структуру.

Код для воспроизведения вопроса:

#include <iostream>
#include <stdint.h>

union float16_and_int16
{
    struct
    {
        uint16_t    Mantissa : 10;
        uint16_t    Exponent : 5;
        uint16_t    Sign : 1;
    } Components;

    uint16_t bitMask;
};

int main()
{
    uint16_t input = 0x153F;

    float16_and_int16 result;
    result.bitMask = input;

    printf("Mantissa: %#010x\n", result.Components.Mantissa);
    printf("Exponent: %#010x\n", result.Components.Exponent);
    printf("Sign:     %#010x\n", result.Components.Sign);
    return 0;
}

В этом примере я ожидал бы, что моя Мантисса будет 0x00000054, показатель степени будет 0x0000001F, а знак 0x00000001

Вместо этого я получаю Мантиссу: 0x0000013f, Экспонент: 0x00000005, Знак: 0x00000000

Это означает, что из моей битовой маски сначала был взят Знак (первый бит), следующие 5 битов для экспоненты, затем 10 битов для мантиссы, так что порядок обратный тому, что я хотел. Почему это происходит?

Ответы [ 2 ]

5 голосов
/ 04 июня 2019

Хуже всего то, что другой компилятор может дать ожидаемый порядок. Стандарт никогда не определял детали реализации для битовых полей и, в частности, порядок. Как обычно, это обоснование того, что это деталь реализации, и что программисты не должны полагаться на это и не зависеть от этого.

Недостатком является то, что невозможно использовать битовые поля в кросс-языковых программах, и что программисты не могут использовать битовые поля для обработки данных, имеющих хорошо известные битовые поля (например, в заголовках сетевых протоколов), потому что это слишком сложно, чтобы убедиться, как реализация их обработает.

По этой причине я всегда думал, что это просто неиспользуемая функция, и я использую битовую маску только для неподписанных типов вместо битовых полей. Но эта последняя часть не более, чем мое собственное мнение ...

2 голосов
/ 04 июня 2019

Я бы сказал, что ваш input неверен, для этого компилятора в любом случае. Вот как выглядит float16_and_int16 ордер.

 sign   exponent  mantissa
 [15]   [14:10]    [9:0]

или

SGN |  E  X  P  O  N  E  N  T|     M  A   N   T   I   S   S   A                |
 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |

если input = 0x153F, то bitMask ==

SGN |  E  X  P  O  N  E  N  T|     M  A   N   T   I   S   S   A                |
 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
 0     0    0    1    0    1    0    1    0    0    1    1    1    1    1    1

так

MANTISSA == 0100111111  (0x13F)
EXPONENT == 00101 (0x5)
SIGN == 0 (0x0)

Если вы хотите, чтобы мантисса была 0x54, экспонента 0x1f и знак 0x1, вам нужно

SGN |  E  X  P  O  N  E  N  T|     M  A   N   T   I   S   S   A                |
 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
 1     1    1    1    1    1    0    0    0    1    0    1    0    1    0    0

или

input = 0xFC64
...