Эффективная проверка деления поплавка на ноль для встроенного приложения - PullRequest
0 голосов
/ 11 апреля 2019

Для встроенного приложения SW на микроконтроллере мне нужно написать какой-то специальный оператор деления для float. Решение, которое я хочу использовать, кратко описано ниже.

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

Кто-нибудь имел опыт применения различных подходов для управления делением на нуль, которое может быть эффективным / оптимизированным для встраиваемых приложений?

typedef union MyFloatType_
{
   unsigned long  Ulong;
   float          Float;
}MyFloatType;

float div_float(float a, float b)
{ 
   float numerator = a;
   MyFloatType denominator;
   denominator.Float= b;
   float result = 0.0f;

   if((denominator.Ulong & 0x7fffffffUL) != 0UL)
   {
      result = numerator / denominator.Float;

   } else 
   {
       /*handle devision by zero, for example:*/
       result = 0.0f;
   }
return result;
}

Ответы [ 2 ]

2 голосов
/ 11 апреля 2019

В большинстве приложений будет какое-то значение, ниже которого число с плавающей запятой "может также быть" нулем. Например, рассмотрим что-то вроде:

float intensity(float dx, float dy)
{
  float result = 1/(dx*dx + dy*dy);
  if (result > 65535.0f) result = 65535.0f;
  return result;
}

Если делитель меньше 1 / 65535.0f, то в случаях, когда расстояние не равно нулю, функция должна возвращать 65535.0f независимо от фактического значения делителя, и такое поведение, вероятно, будет полезно, даже если оно это ноль. Таким образом, функция может быть переписана как:

float intensity(float dx, float dy)
{
  float distSq = dx*dx + dy*dy;
  if (distSq <= (1.0f/65535.0f))
    return 65535.0;
  else
    return 1/distSq;
}

Обратите внимание, что обработка кода в этом случае может быть очень несовершенной. Хотя это не является проблемой для 65535.0f, в частности, могут быть случаи, когда distSq точно равно обратной величине максимального значения, но обратная величина меньше максимальной величины. Например, если максимальное значение равно 46470.0f, а distSq равно 0,0000215238924f, правильный результат будет 46459.9961f, но функция вернет 46470.0f. Такие вопросы вряд ли будут создавать проблемы на практике, но нужно знать о них. Обратите внимание, что если для сравнения использовалось значение «меньше», чем «меньше или меньше или равно», а максимум был 46590,0f, значение distSq 0,0000214642932f дало бы результат 46589,0039, что превышает максимум.

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

1 голос
/ 11 апреля 2019

Это не быстрее, но потенциально намного медленнее. Он должен переместить число с плавающей точкой в ​​регистр целых чисел (возможно, записав его в память и прочитав его обратно), а затем выполнить целочисленную операцию. Просто выполните == 0.0f, и вместо этого будет выполнено сравнение с плавающей запятой, и это самый эффективный способ.

Если вы хотите, чтобы он был «высокопроизводительным», попробуйте следующее:

float div_float(float a, float b)
{ 
   b = b == 0.0f ? 1.0f : b;
   return a / b;
}

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

...