У меня есть программа обработки изображений, которая использует вычисления с плавающей запятой. Однако мне нужно перенести его на процессор, который не имеет поддержки с плавающей запятой. Итак, я должен изменить программу, чтобы использовать вычисления с фиксированной точкой. Для этого мне нужно правильное масштабирование тех чисел с плавающей запятой, для которых мне нужно знать диапазон всех значений, включая промежуточные значения вычислений с плавающей запятой.
Есть ли способ, при котором я просто запускаю программу, и он автоматически дает мне диапазон всех вычислений с плавающей запятой в программе? Попытка определить диапазоны вручную была бы слишком обременительной, поэтому, если есть какой-то инструмент для этого, это было бы здорово!
Вы можете использовать некоторую «измерительную» замену для вашего плавающего типа, по этим направлениям (живой пример):
template<typename T>
class foo
{
T val;
using lim = std::numeric_limits<int>;
static int& min_val() { static int e = lim::max(); return e; }
static int& max_val() { static int e = lim::min(); return e; }
static void sync_min(T e) { if (e < min_val()) min_val() = int(e); }
static void sync_max(T e) { if (e > max_val()) max_val() = int(e); }
static void sync(T v)
{
v = std::abs(v);
T e = v == 0 ? T(1) : std::log10(v);
sync_min(std::floor(e)); sync_max(std::ceil(e));
}
public:
foo(T v = T()) : val(v) { sync(v); }
foo& operator=(T v) { val = v; sync(v); return *this; }
template<typename U> foo(U v) : foo(T(v)) {}
template<typename U> foo& operator=(U v) { return *this = T(v); }
operator T&() { return val; }
operator const T&() const { return val; }
static int min() { return min_val(); }
static int max() { return max_val(); }
};
использоваться как
int main ()
{
using F = foo<float>;
F x;
for (F e = -10.2; e <= 30.4; e += .2)
x = std::pow(10, e);
std::cout << F::min() << " " << F::max() << std::endl; // -11 31
}
Это означает, что вам нужно определить псевдоним (скажем, Float
) для вашего плавающего типа (float
или же double
) и используйте его последовательно на протяжении всей вашей программы. Это может быть неудобно, но в конечном итоге может оказаться полезным (потому что тогда ваша программа будет более общей). Если ваш код уже основан на плавающем типе, даже лучше.
После этой параметризации вы можете переключить вашу программу в режим «измерения» или «выпуска», определив Float
быть либо foo<T>
или же T
, где T
твой float
или же double
,
Хорошо, что вам не нужны внешние инструменты, ваш собственный код выполняет измерения. Плохо то, что, как в настоящее время разработано, он не будет ловить все промежуточные результаты. Вы должны будете определить все (например, арифметические) операторы на foo
за это. Это можно сделать, но нужно еще немного поработать.
Неверно, что вы не можете использовать код с плавающей запятой на оборудовании, которое не поддерживает с плавающей запятой — компилятор предоставит программные процедуры для выполнения операций с плавающей запятой — они могут быть довольно медленными — но если это достаточно быстро для вашего приложения, это это путь наименьшего сопротивления.
Вероятно, проще всего реализовать класс типов данных с фиксированной запятой и заставить его функции-члены обнаруживать переполнение / недополнение в качестве опции отладки (поскольку проверка в противном случае замедлит ваш код).
Я предлагаю вам взглянуть на Библиотека математики C ++ с фиксированной точкой Энтони Уильямса. Он находится в C ++ и определяет fixed
класс с обширной функцией и перегрузкой операторов, так что его можно в значительной степени использовать, просто заменив float
или же double
в вашем существующем коде с fixed
, Оно использует int64_t
в качестве базового целочисленного типа данных, с 34 целочисленными битами и 28 дробными битами (34Q28), он подходит примерно для 8 десятичных знаков и более широкого диапазона, чем int32_t
,
Он не имеет проверки недополнения / переполнения, которую я предложил, но это хорошая отправная точка для добавления вашей собственной.
В 32-битном ARM эта библиотека работает примерно в 5 раз быстрее, чем программные с плавающей запятой, и сопоставима по производительности с блоком VFP ARM для C-кода.
Обратите внимание, что sqrt()
Функция в этой библиотеке имеет низкую точность при очень малых значениях, поскольку она теряет младшие биты в промежуточных вычислениях, которые могут быть сохранены. Это можно улучшить, заменив код версией, которую я представил в этот вопрос.
Для автономных программ на C вы можете использовать Анализ стоимости Frama-C например, чтобы получить диапазоны для переменных с плавающей точкой h
ниже:
И переменная g
вычислено из h
:
Существует язык спецификации для описания диапазонов входных данных (информация, без которой трудно сказать что-либо информативное). В приведенном выше примере я использовал этот язык, чтобы указать, какую функцию float_interval
ожидалось сделать:
/*@ ensures \is_finite(\result) && l <= \result <= u ; */
float float_interval(float l, float u);
Frama-C проще всего установить в Linux, поскольку бинарные пакеты Debian и Ubuntu для последней (но обычно не самой последней) версии доступны в дистрибутиве.
Если бы вы могли опубликовать свой код, это помогло бы определить, является ли этот подход реалистичным. Если ваш код, например, C ++ (ваш вопрос не говорит, что он помечен несколькими языковыми тегами), то текущая версия Frama-C не поможет, так как она принимает только программы на C.