[sisyphus] Странности при переходе объединении двух целых в вещественное на x86_64

Yuriy Kashirin yura на emict.com
Пт Авг 7 10:28:53 MSD 2009


On Пятница 07 августа 2009, Roman Savochenko wrote:
> Kirill A. Shutemov wrote:
> >> Имеется некая целевая задачка собрать из двух 16-разрядных целых
> >> вещественное (float), 32 разряда.
> >> Казалось-бы тривиальная задача, которая решается кодом типа
> >> int w1 = 62915, w2 = 16456;
> >> ui32 vl = ((w2&0xffff)<<16) | w1&0xffff;
> >> //sleep(1);
> >> printf("TEST 00: %f\n",*(float*)&vl);
> >>
> >> И как ожидалось на x86_32 он работает корректно при различной
> >> нагрузке. А вот на x86_64 замечается ситуация когда вместо 3.14
> >> получаем ноль. Причём в тестовой программке с единственным
> >> потоком всё работает нормально, а на высоконагруженном процессе
> >> с десятками потоков, из которых около пяти работают с периодом
> >> 5мс. устойчиво получатся 0.
> >> Если раскомментирую sleep, то получаю номальный результат 3.14.
> >>
> >> Кто нибуть может такое поведение объяснить?
> >
> > Похоже, вы нарушили strict aliasing. Попробуйте собрать с
> > -Wstrict-aliasing=2. Если будет ругаться, то это наверно оно(см.
> > оговорку в мане насчёт этой опции).
> >
> > Нарушение strict aliasing может сломать некоторые оптимизации.
> > Два выхода -- или собрать с -fno-strict-aliasing или переписать
> > код корректней.
>
> Я его записывал уже тремя различными способами с одинаковым
> результатом. :)
>
> int w1 = 62915, w2 = 16456;
> float vl = 0;
> *(ui16*)&vl = w1;
> *(((ui16*)&vl)+1) = w2;
> printf("TEST 00: %f\n",vl);
>
> и
>
> int w1 = 62915, w2 = 16456;
> char vl[4];
> *(ui16*)vl = w1;
> *(((ui16*)vl)+1) = w2;
> printf("TEST 00: %f\n",*(float*)vl);

Попробуйте так:

    int w1 = 62915, w2 = 16456;
    union {
      ui32 wl;
      float f;
    } u;
    u.wl = ((w2&0xffff)<<16) | w1&0xffff;
    printf("TEST 00: %f\n", u.f);


-- 
 Best regards
 Yuriy Kashirin




Подробная информация о списке рассылки Sisyphus