Как использовать возвращаемое значение прямо из функции в качестве цепочки битов вместо отформатированного числа?

Как получить эффект intVariable = *(int*) &floatVariable прямо из функции вместо того, чтобы сначала сохранять выходные данные функции в фиктивной переменной?

т.е.

float functionWithFloatTypedReturn(int input) {
    [enter code here]
    return serialStringStruct.floatRetVal;
}
...
intVariable = [desired code] functionWithFloatTypedReturn(commandCode);

Справочная информация: у меня есть функция, которая читает из последовательного ответа, предоставленного чужим оборудованием. Существует устаревший код и интерес к языковым возможностям, которые мотивируют дальнейшее использование единственной функции. Если функция возвращает числовое значение "1", это может быть 0x3f800000 или 0x1, в зависимости от входа в функцию (который определяет, какая команда передается аппаратному обеспечению). Последовательная строка, поступающая от аппаратного обеспечения, разбивается независимо от типа в структуру при вызове memcpy.

3 ответа

Решение
intVariable = (union { float f; int i; }) { functionWithFloatTypedReturn(commandCode) } .i;

При этом используется составной литерал, содержащий объединение, для повторной интерпретации байтов значения (что поддерживается в C 1999 и более поздних версиях).

Обратите внимание, что обычно рекомендуется использовать целое число без знака, а не int чтобы получить доступ к битам кодировок с плавающей запятой, потому что целые числа без знака менее подвержены определенным проблемам, таким как проблемы знака со сдвигом битов.

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

Этот код выполняет реинтерпретацию без промежуточной переменной C. Однако это не означает, что он будет более эффективным в реализации машинного языка.

Вы не можете, отчасти потому, что многие компиляторы возвращают значения с плавающей запятой в регистре с плавающей запятой (или в стеке FPU), и поэтому они должны быть сохранены в "обычной" памяти, прежде чем вы сможете получить к ним доступ побитовым способом.

Вы можете использовать встроенную функцию, чтобы сделать преобразование. Обратите внимание, что Стандарт C не определяет эффект обработки битового шаблона с плавающей запятой как целочисленного битового шаблона (или наоборот), но это нормально, чтобы зависеть от вашей реализации здесь, так как, ну, вы должны. Просто поместите это в какой-нибудь файл "зависит от реализации" (заголовок, модуль, что угодно).

(Я бы посоветовал найти способ немного изменить его, чтобы скрыть аппаратную изменчивость от остальной части кода.)

Если у вас есть компилятор, который поддерживает стандарт C99, вы можете использовать составной литерал:

intVariable = *(int *)&(float){ functionWithFloatTypedReturn(commandCode) };

Как отмечает Эрик Постпищил в комментариях, union не имеет таких проблем с псевдонимами, которые имеет тип указателя:

intVariable = (union { float f; int i; }){ .f = functionWithFloatTypedReturn(commandCode) }.i;

Это предполагает лучший подход, однако - изменить floatRetVal член структуры к union содержащий float и intи прямо вернуть это union введите из функции:

union retval {
    float f;
    int i;
};

union retval functionWithUnionTypedReturn(int input) {
    [enter code here]
    return serialStringStruct.floatOrIntRetVal;
}

Тогда код, который знает, что получит float возвращаемый тип использует:

floatVariable = functionWithUnionTypedReturn(commandCode).f;

и код, который знает, что он получит int возвращаемый тип использует:

intVariable = functionWithUnionTypedReturn(commandCode).i;
Другие вопросы по тегам