То, что вы просите, может быть выполнено с различной степенью частичной переносимости, но не абсолютным, если вы не хотите запускать исходный файл через собственный инструмент предварительной обработки во время сборки, чтобы снизить точность.Если это вариант для вас, он, вероятно, ваш лучший.
Если не считать этого, я собираюсь предположить, по крайней мере, что ваши типы с плавающей запятой - это база 2 и подчиняются семантике Приложения F / IEEE.Это должно быть разумным допущением, но последнее неверно для gcc на платформах (включая 32-разрядные x86) с расширенной точностью в соответствии с профилем соответствия стандартам по умолчанию;вам нужно -std=cNN
или -fexcess-precision=standard
, чтобы исправить это.
Один из подходов состоит в том, чтобы сложить и вычесть степень двух, выбранную, чтобы вызвать округление с желаемой точностью:
#define FP_REDUCE(x,p) ((x)+(p)-(p))
К сожалению,это работает с абсолютной точностью, а не с относительной, и требует знания правильного значения p
для конкретного x
, которое будет равно значению ведущего места base-2 x
, умноженного на 2, повышенного досила FLT_MANT_DIG
минус биты точности, которую вы хотите.Это не может быть оценено как константное выражение для использования в качестве инициализатора, но вы можете записать его в терминах FLT_EPSILON
и, если вы можете предположить, что C99 + - вставка токена препроцессора для формирования шестнадцатеричного литерала с плавающей запятой, получая правильное значениедля этого фактора.Но вам все равно нужно знать силу двоичного числа для старшей цифры x
;Я не вижу способа извлечь это как константное выражение.
Редактировать: Я считаю, что это можно исправить, чтобы не требовалась абсолютная точность, а скорее автоматически масштабировалось до значения, но это зависит от правильности незавершенного производства.См. Существует ли правильное выражение константы с точки зрения числа с плавающей запятой для его msb? .Если это сработает, я позже интегрирую результат с этим ответом.
Другой подход, который мне нравится, если ваш компилятор поддерживает составные литералы в статических инициализаторах и если вы можете предполагать представления типов IEEE, использует объединение и маскирование битов:
union { float x; uint32_t r; } fr;
#define FP_REDUCE(x) ((union fr){.r=(union fr){x}.r & (0xffffffffu<<n)}.x)
где n
- количество бит, которое вы хотите отбросить.Это будет округлять до нуля, а не до ближайшего;если вы хотите сделать округление до ближайшего, это должно быть возможно путем добавления соответствующей константы к младшим битам перед маскированием, но вы должны позаботиться о том, что произойдет, когда сложение переполнится в биты экспоненты.