самый быстрый c арифметический метод для целых - PullRequest
4 голосов
/ 03 марта 2011

Я пишу программу переменного тока.У меня есть целое число без знака (16 бит), значение которого может быть любым в любое время, и у меня есть символ со знаком (8 бит), значение которого может быть любым в любое время, в пределах очевидных ограничений типов данных.Мне нужно добавить подписанный символ к целому без знака, результатом будет неподписанное целое, и если значение переполняется либо за 0xFFFF, либо ниже 0x00, мне нужен результат, равный пределу (0x00 или 0xFFFF).Я хочу знать, что будет самым быстрым подходом к этому?Мой подход показан ниже, но он использует длинный тип данных и, следовательно, длинную двоичную арифметику, поэтому я предполагаю, что есть более быстрый способ ...

long i;
unsigned int result;

i = someUINT + someCHAR;

if(i <= 0) 
{
    result = 0;
}
else if(i >= 0xFFFF)
{
    result = 0xFFFF;
}
else 
{
    result = (unsigned int)i;
}

РЕДАКТИРОВАТЬ: я использую 16-битный MCU (серия PIC24HJ) и компилятор Microchip C30.

Ответы [ 7 ]

3 голосов
/ 04 марта 2011

Почти наверняка правильный ответ -

if(i <= 0) 
{
    result = 0;
}
else if(i >= 0xFFFF)
{
    result = 0xFFFF;
}
else 
{
    result = (unsigned int)i;
}

Профилируйте приложение, и если это окажется узким местом (в чем я очень, очень сомневаюсь), то перепишите его.


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

0 голосов
/ 21 марта 2011

Я думаю, что самым быстрым будет что-то вроде:

  UInt16 uival;
  Int8 sbval;
  UInt16 result;

  result = uival + sbval;
  if (uival & 0x8000)  /* Only worry about max-val overflow */
  {
    if (result = 65280) /* Underflow */
      result = 0;
  }

Все немного упрощено, потому что любые переполнения могут происходить только в небольшой части числового диапазона. Если бы добавление было 16 битами, было бы необходимо проверить разницу между исходным uint16 и результатом, чтобы увидеть, было ли переполнение; поскольку добавление составляет всего 8 бит, это не нужно. Я не использовал части PIC24xx, поэтому я не знаю, быстрее ли тестирует 256 или 65280, чем другие значения, но на 8-битных частях это точно должно быть.

0 голосов
/ 04 марта 2011
result = someUINT + someCHAR;
if (someCHAR > 0)
{
   if (result < someCHAR)
   {
      result = 0xFFFF;
   }
}
else if (result > someUINT)
{
   result = 0;
}
0 голосов
/ 04 марта 2011

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

Многие процессоры (включая, я полагаю, PIC24) имеют инструкции «насыщающего добавления», которыевыполнить именно эту операцию.Обычно быстрее всего написать ассемблер, который использует эту инструкцию специально, но нет причин делать это, если у вас нет доказательств того, что функция должна быть быстрее.

0 голосов
/ 04 марта 2011

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

    long i;
    i= char + int;
    if((i & 0xFFFF) == i){
      return (int)i;
    }
    else if(i < 0)
    {
      return 0;
    }
    else
    {
      return 0xFFFF;
    }
0 голосов
/ 04 марта 2011

Этот алгоритм работает только в дополнении 2.

При проверке дополнения со знаком на переполнение результат должен иметь тот же знак, что и хотя бы один из операндов.Этот случай оказывается лишь немного другим;если результат переворачивает бит «знак», то все в порядке, если оба операнда имеют одинаковый бит «знак».И, конечно, вычисление границ без знака проще!

uint16_t UIntPlusChar(uint16_t u, char ch)
{
  int16_t i = (int16_t)u;
  int16_t p = i + ch;
  if ((ch ^ i) < 0 && (p ^ i) < 0)
    p = i >> 15;
  return (uint16_t)p;
}
0 голосов
/ 03 марта 2011

Вы можете избежать длительной проверки перед добавлением:

if(0xFFFF - someUINT < someCHAR) {
  return 0xFFFF;
} else {
  return someUINT + someCHAR;
}

Конечно, если вам ДЕЙСТВИТЕЛЬНО нужно, чтобы это было БЫСТРО, превратите это во встроенную функцию или макрос и начните сборку.

...