независимый от платформы способ снизить точность значений констант с плавающей точкой - PullRequest
0 голосов
/ 03 декабря 2018

Вариант использования:

У меня есть несколько больших массивов данных, содержащих константы с плавающей запятой, которые.Файл, определяющий этот массив, генерируется, и шаблон может быть легко адаптирован.

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

Поскольку я не хочу изменять другой исходный код, кроме сгенерированного файла, я ищу способ уменьшить точность констант.

Я бы хотел ограничить мантиссу фиксированнойколичество бит (установите младшие 0).Но поскольку литералы с плавающей запятой находятся в десятичном виде, возникают некоторые трудности, определяя числа таким образом, чтобы двоичное представление содержало все нули в младших битах мантиссы.

В лучшем случае было бы что-то вроде:

#define FP_REDUCE(float)  /* some macro  */

static const float32_t veryLargeArray[] = {
  FP_REDUCE(23.423f), FP_REDUCE(0.000023f), FP_REDUCE(290.2342f),
  // ... 
};

#undef FP_REDUCE

Это должно быть сделано во время компиляции и не должно зависеть от платформы.

Ответы [ 2 ]

0 голосов
/ 03 декабря 2018

Следующее использует алгоритм расщепления Вельткампа-Деккера для удаления n битов (с округлением) из x , где p =2 n (например, чтобы удалить восемь битов, используйте 0x1p8f для второго аргумента).Приведения к float32_t приводят результаты к этому типу, так как в противном случае стандарт C позволяет реализациям использовать большую точность в выражениях.(Двойное округление может привести к неверным результатам в теории, но этого не произойдет, если float32_t является базовым 32-разрядным двоичным форматом IEEE, и реализация C вычисляет это выражение в этом формате или в 64-разрядном формате или шире, так какпервый - желаемый формат, а второй достаточно широкий, чтобы точно представлять промежуточные результаты.)

Предполагается двоичная плавающая точка IEEE-754 с округлением до ближайшего.Переполнение происходит, если x • ( p + 1) округляется до бесконечности.

#define RemoveBits(x, p) (float32_t) (((float32_t) ((x) * ((p)+1))) - (float32_t) (((float32_t) ((x) * ((p)+1))) - (x))))
0 голосов
/ 03 декабря 2018

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

Если не считать этого, я собираюсь предположить, по крайней мере, что ваши типы с плавающей запятой - это база 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 - количество бит, которое вы хотите отбросить.Это будет округлять до нуля, а не до ближайшего;если вы хотите сделать округление до ближайшего, это должно быть возможно путем добавления соответствующей константы к младшим битам перед маскированием, но вы должны позаботиться о том, что произойдет, когда сложение переполнится в биты экспоненты.

...