【问题标题】:fastest c arithmetic method for ints最快的整数 c 算术方法
【发布时间】:2011-07-08 08:20:32
【问题描述】:

我正在编写一个 c 程序。我有一个无符号整数(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 编译器。

【问题讨论】:

  • 这应该在什么硬件上运行?这个问题的答案完全取决于硬件平台。
  • 如果使用long(我认为在这个所谓的处理器上会是字长的两倍?)太慢,那么您可能需要降级到汇编级别,以便检查溢出标志。
  • @Mike Caron:我并不是说使用 long 太慢了,我只是想知道是否有更快的方法。
  • @Mike Caron:您能否发布一个带有基本示例的答案。如果它不完全适合我的处理器,我不会难过。
  • @Jordan:我很想发表一个答案,但我不做机器组装。 (.NET 程序集,当然……;)

标签: c math binary overflow


【解决方案1】:

几乎可以肯定,正确答案是

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

分析应用程序,如果这是一个瓶颈(我非常非常怀疑),然后重写它。


现代编译器非常擅长为这样的代码编写无分支条件,所以只需以最有意义的方式编写它,让编译器完成它的工作。不要混淆编译器和任何可怜的人将来必须通过使用一些令人费解的位摆弄技巧来阅读此代码。

【讨论】:

  • @Jordan:另外,虽然我不知道您的具体用例,但您的意思可能是使用 UINT_MAX 而不是 0xFFFF
【解决方案2】:

您可以避免在添加之前进行长时间检查:

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

当然,如果你真的需要它是 FAST,把它变成一个内联函数或宏然后去汇编。

【讨论】:

  • 别忘了 someCHAR 可能是负数:someUINT = 10; someCHAR = -42; .... hmmm ... 你需要格外小心“积分提升”和“通常的算术转换”!!!
【解决方案3】:

此算法仅适用于 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;
}

【讨论】:

    【解决方案4】:

    哇,我喜欢这样的东西。这是我的刺,假设大多数时候它会落在边界之间试试这个

        long i;
        i= char + int;
        if((i & 0xFFFF) == i){
          return (int)i;
        }
        else if(i < 0)
        {
          return 0;
        }
        else
        {
          return 0xFFFF;
        }
    

    【讨论】:

    • 为什么添加一个操作会更快?
    • @BlueRaja, if ((i &amp; 0xFFFF) == i) 是 2 条带分支的超快(仅限寄存器)指令,这也应该是可以预测的。如果是这样的话,那就是保持边界,解决方案很好! (不仅好,而且最重要的是,提供);编辑,现在我看到 CPU 只有 16 位,不是那么热
    • @bestsss: ...是的,i &gt;= 0xFFFF 是一条带有分支的指令。不仅如此,它的意图也更加清晰。
    • @BlueRaja, (i &amp; 0xFFFF) == i 确保 i 的值在 0-0xffff 范围内,这就是诀窍;这是最常见的情况,如果预测得当(应该),它不需要任何成本。相反,您需要 2 个比较分支来确保相同的范围。这种推测可能对 OP 所说的 16 位 CPU 无效,我不知道它是如何工作的,而且它没有 32 位寄存器来保存i的溢出值@
    • 听上去是针对嵌入式系统的。我曾使用过这些编译器,但它们并不像人们想象的那么先进。无论如何,我并没有声称知道有关此 CPU 及其分支预测的所有信息,这只是我解决问题的尝试。
    【解决方案5】:

    最快的方法几乎总是利用在可移植 C 代码中无法描述的特定于处理器的功能。编写明显正确且有效的可移植代码,让编译器完成它应该做的事情。如果您有特定的基准数据表明这确实必须更快,请实施一个针对特定处理器的额外调整版本。

    许多处理器(包括,我相信 PIC24)都有“饱和加法”指令,可以完全执行此操作。最快的方法通常是编写专门使用该指令的程序集,但没有理由这样做,除非您有证据表明该函数需要更快。

    【讨论】:

      【解决方案6】:
      result = someUINT + someCHAR;
      if (someCHAR > 0)
      {
         if (result < someCHAR)
         {
            result = 0xFFFF;
         }
      }
      else if (result > someUINT)
      {
         result = 0;
      }
      

      【讨论】:

        【解决方案7】:

        我猜最快的应该是这样的:

        UInt16 uival; Int8 sbval; UInt16 结果; 结果 = uival + sbval; if (uival & 0x8000) /* 只担心 max-val 溢出 */ { if (result = 65280) /* 下溢 */ 结果 = 0; }

        事情有点简化,因为任何溢出都只能发生在数值范围的一小部分。如果加数为 16 位,则需要测试原始 uint16 与结果的差异,看是否溢出;因为加数只有 8 位,所以不需要。我没有使用过 PIC24xx 部件,所以我不知道测试 256 或 65280 是否比其他值更快,但在 8 位部件上肯定应该是。

        【讨论】:

          猜你喜欢
          • 2012-02-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-07
          • 2015-03-27
          • 1970-01-01
          • 1970-01-01
          • 2019-02-27
          相关资源
          最近更新 更多