Есть ли способ перенести константы с плавающей запятой лучшего типа с микро на другое?

В моем конкретном случае я сначала разработал программу для работы на микроконтроллере Texas Instruments (TMS320F28335). Это был симулятор синхронного генератора в реальном времени, поэтому ему нужно было выполнять большое количество операций с плавающей запятой. Я не указывал никакого суффикса для констант с плавающей запятой, поэтому они обрабатывались как двойные (я думаю, это то, что говорят стандарты C), но компилятор, предоставленный Texas Instruments, реализует эти двойные числа как 32-битные числа с плавающей запятой, поэтому FPU микроконтроллера (см. Таблицу 6-1 из руководства пользователя компилятора) скажем так, эффективно. Затем мне пришлось перенести эту программу на BeagleBone Black со встроенным Linux (исправленным для требований реального времени). Конечно, все константы снова обрабатывались компилятором (GCC) как удвоения, но в данном случае это означало не 32-битные числа с плавающей запятой, а 64-битные числа с плавающей запятой. Я не совсем понимаю, как работает FPU ARM Cortex A8, но, насколько я читал (и поскольку это 32-битный процессор), производительность была бы улучшена, если бы эти константы с плавающей запятой обрабатывались как 32-битные числа с плавающей запятой. . Итак, все это привело меня к вопросу: есть ли способ сделать более переносимые константы с плавающей запятой «лучшего типа»? В этом случае я бы решил проблему, добавив суффикс «f» к каждой константе, потому что оба процессора более эффективны (я думаю) при обработке с плавающей запятой, но если я разрабатываю что-то на ПК с amd64 и хочу портировать это к 32-битному микроконтроллеру, есть ли способ добавить суффикс, который можно было бы изменить на «f» для 32-битных микроконтроллеров и «l» для ПК amd64? Я подумал о чем-то вроде этого (конечно, это не работает):

заголовочный файл, зависящий от архитектуры, для 32-битного микроконтроллера:

#define BEST_TYPE f
.
.
.

заголовочный файл, зависящий от архитектуры, для ПК amd64:

#define BEST_TYPE l
.
.
.

независимый от архитектуры исходный файл:

.
.
.
a = b * 0.1BEST_TYPE;
.
.
.

Чтобы уточнить, под «лучшим типом» я имел в виду более точный числовой тип данных, поддерживаемый FPU микропроцессора.


person rocomanmelli    schedule 14.11.2019    source источник
comment
Самый эффективный или самый точный? Ваш вопрос, кажется, хочет, чтобы оба сразу. double должен быть самым точным, переносимым.   -  person Lundin    schedule 14.11.2019
comment
Наиболее точный числовой тип данных, но напрямую поддерживаемый FPU, поэтому эффективность будет главной проблемой между эффективностью и точностью.   -  person rocomanmelli    schedule 14.11.2019
comment
Вы можете определить его как макрос с параметром: #define BEST_TYPE(x) ((float)(x)). Затем вы можете использовать a = b * BEST_TYPE(0.1); в своем независимом от архитектуры исходном файле.   -  person Ian Abbott    schedule 14.11.2019
comment
Хорошо, это вариант, о котором я думал. Я сомневаюсь, выполняется ли приведение во время компиляции или во время выполнения? Большинство компиляторов должны решить это во время компиляции, верно? Нужно ли для этого задавать какой-то параметр (несмотря на уровень оптимизации)?   -  person rocomanmelli    schedule 14.11.2019
comment
@IanAbbott: это подлежит двойному округлению. Десятичное число должно быть сначала преобразовано в double, а затем в float. Это может дать менее точный результат, чем прямое преобразование в float. Лучше добавить суффикс для обозначения типа, например, #define BEST_TYPE(x) x##f.   -  person Eric Postpischil    schedule 14.11.2019


Ответы (1)


Вопрос требует чего-то похожего на макросы в C 2018 7.20.4, «Макросы для целочисленных констант». Эти макросы, такие как UINT64_C(value), расширяются до целочисленных констант, подходящих для инициализации объектов с соответствующим типом.

Если мы посмотрим на <stdint.h>, поставляемый с Xcode 11 для macOS 10.14, мы увидим, что реализации просто добавляют суффикс, который указывает тип (или ничего не делает, если значение будет иметь желаемый тип по умолчанию):

#define UINT8_C(v)   (v)
#define UINT16_C(v)  (v)
#define UINT32_C(v)  (v ## U)
#define UINT64_C(v)  (v ## ULL)

Мы можем использовать это как руководство для аналогичного макроса для типов с плавающей запятой, используя один из:

#define MyFloatType_C(v)   (v ## f)
#define MyFloatType_C(v)   (v)
#define MyFloatType_C(v)   (v ## l)

Какое определение макроса использовать, можно выбрать с помощью различных средств времени компиляции, таких как тестирование макросов препроцессора (которые либо встроены в компилятор для описания цели, либо явно передаются в командной строке).

person Eric Postpischil    schedule 14.11.2019
comment
Именно то, что я хотел знать. Благодарю вас! - person rocomanmelli; 14.11.2019