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

Kirill A. Shutemov kirill на shutemov.name
Пт Авг 7 10:31:39 MSD 2009


2009/8/7 Roman Savochenko <rom_as at diyaorg.dp.ua>:
> 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 или переписать код корректней.
>>
>
> Я его записывал уже тремя различными способами с одинаковым результатом. :)

И во всех трёх вариантах нарушили strict aliasing. Используйте union.

>
> 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);
>
> С уважением, Роман
>
> _______________________________________________
> Sisyphus mailing list
> Sisyphus at lists.altlinux.org
> https://lists.altlinux.org/mailman/listinfo/sisyphus
>


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