Грешка при строги псевдоними за преобразуване от fp16 към fp32

Имам функция за конвертиране на fp16 в fp32

static float fp16_to_fp32(const short in){
    signed int t1, t2, t3;
    float out = 0;

    t1 = (in & 0x7fff) << 13 + 0x38000000;
    t2 = (in & 0x8000) << 16;
    t3 = in & 0x7c00;
    
    t1 = (t3==0 ? 0 : t1);
    t1 |= t2;

    *((unsigned int*)&out) = t1;
    
    return out;
}

грешка: дереферирането на въведен-набит указател ще наруши правилата за стриктно псевдоним [-Werror=strict-aliasing] в ((unsigned int)&out) = t1;< /em>

Как мога да разреша това? (не може да промени типа на аргумента in )


person Its me    schedule 20.04.2021    source източник
comment
За C е позволено да се прави игра на думи чрез съюзи. В противен случай можете да използвате char указатели или масиви като междинна стъпка (напр   -  person Some programmer dude    schedule 20.04.2021
comment
@NateEldredge Защо sizeof(short)?   -  person MikeCAT    schedule 20.04.2021
comment
@MikeCAT: Съжалявам, гледах in вместо t1. Във всеки случай вашият отговор обхваща всичко.   -  person Nate Eldredge    schedule 20.04.2021
comment
Ако това е за модерни x86 процесори, може също да се интересувате от _cvtsh_ss и свързаните с него вътрешни елементи   -  person harold    schedule 20.04.2021
comment
Основен бъг: + има по-висок приоритет от ‹‹. Съмнявам се, че искате да преместите 13 + 0x38000000 бита наляво... приличен компилатор ще ви каже толкова много.   -  person Lundin    schedule 20.04.2021
comment
Също така не може да възпроизведе грешката на gcc. Кой компилатор, порт и опции използвате?   -  person Lundin    schedule 20.04.2021


Отговори (3)


Можете да използвате memcpy() за копиране на данни.

Също така имайте предвид, че операторът + има по-висок приоритет от оператора <<, така че редът t1 = (in & 0x7fff) << 13 + 0x38000000; няма да работи според очакванията.

#include <string.h> /* for memcpy() */

static float fp16_to_fp32(const short in){
    signed int t1, t2, t3;
    float out = 0;

    t1 = ((in & 0x7fff) << 13) + 0x38000000; /* add parenthesis */
    t2 = (in & 0x8000) << 16;
    t3 = in & 0x7c00;
    
    t1 = (t3==0 ? 0 : t1);
    t1 |= t2;

    memcpy(&out, &t1, sizeof(out)); /* use memcpy() for copying */
    
    return out;
}
person MikeCAT    schedule 20.04.2021
comment
Благодаря, сработи :) Забравих скобите, докато пишех код тук на платформата. - person Its me; 20.04.2021

Можете да използвате игра на думи през union, за да избегнете строги псевдоними:

union type_punner
{
  unsigned int i;
  float f;
} out = {.i = t1};
return out.f;

Това предполага, че int и float са с еднакъв размер, така че не е много преносим код.

Можете също да компилирате с -fno-strict-aliasing.

person Lundin    schedule 20.04.2021

Опитайте да декларирате out като плаващ указател, след което върнете *out

person syzztem    schedule 20.04.2021
comment
Това също би нарушило строгия псевдоним, ако приемем, че възнамерявате да направите нещо като out = (float *)&t1. - person Nate Eldredge; 20.04.2021