Эффективное преобразование числа с плавающей точкой в ​​строгом переносимом C - PullRequest
0 голосов
/ 30 апреля 2018

Использование этот ответ хорош, потому что он очень переносим, ​​корректен и проходит строгие компиляторы, но он менее эффективен, чем я хочу, и мог бы быть, потому что он не использует x86 инструкция bswap. (Возможно, другие наборы инструкций имеют аналогичные эффективные инструкции.)

Если система, на которой я работаю, поддерживает ntohl(), тогда я ожидаю, что ntohl() использует инструкцию bswap и приблизит меня. ntohl() делает все правильно, но работает только с uint32_t, а не с плавающей точкой. Преобразование между uint32_t и float является типом пробивки и не допускается строгими компиляторами. Создание объединения с плавающей точкой и uint32_t приводит к неопределенному поведению компилятора (за предыдущие посты здесь).

Насколько я понимаю из предыдущих постов, приведение из любого типа указателя к символу * или наоборот явно разрешено. Так что же не так с этим решением? Я еще ни разу не упоминал об этом.

char NetworkOrderFloat[4]; // Assume it contains network-order float bytes

uint32_t HostOrderInt = ntohl(*(uint32_t *)NetworkOrderFloat);

char *Pointer = (char *)&HostOrderInt;

float HostOrderFloat = *(float *)Pointer;

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

1 Ответ

0 голосов
/ 30 апреля 2018

Ваше предложение нарушает «строгие правила псевдонимов», впервые, когда оно делает *(uint32_t *)NetworkOrderFloat. Выражение (uint32_t *)NetworkOrderFloat по-прежнему является адресом массива символов, и доступ к нему с lvalue типа uint32_t противоречит этим правилам. Подробности и другие примеры можно найти в этой статье .

Использование union для преобразования представления числа с плавающей точкой в ​​uint32_t, с другой стороны, не запрещено стандартом C, насколько я знаю. Но вы всегда можете использовать memcpy, если беспокоитесь, что это так.

float NetworkOrderFloat = ...;
uint32_t tmp;
_Static_assert(sizeof(uint32_t)==sizeof(float),"unsupported arch");
memcpy(&tmp, &NetworkOrderFloat, sizeof(float));
tmp = ntohl(tmp);
memcpy(&HostOrderFloat, &tmp, sizeof(float));

Приличный современный компилятор должен скомпилировать memcpy вызовы к нулю и ntohl к bswap.

...