使用位移划分10?
是否可以通过使用纯位移,加法,减法和可能的乘法来将无符号整数除以10? 使用处理器资源非常有限,分裂缓慢。
这是Microsoft编译器在用小整型常量编译分区时所做的。 假设一个32位机器(代码可以相应地调整):
int32_t div10(int32_t dividend) { int64_t invDivisor = 0x1999999A; return (int32_t) ((invDivisor * dividend) >> 32); }
这里要做的是我们乘以1/10 * 2 ^ 32的近似值,然后去除2 ^ 32。 这种方法可以适应不同的除数和不同的位宽。
这对于ia32体系结构非常有用,因为它的IMUL指令将把64位产品放到edx:eax中,并且edx值将是所需的值。 Viz(假设股息通过eax传递,商数通过eax传回)
div10 proc mov edx,1999999Ah ; load 1/10 * 2^32 imul eax ; edx:eax = dividend / 10 * 2 ^32 mov eax,edx ; eax = dividend / 10 ret endp
即使在具有慢乘法指令的机器上,这也将比软件除法更快。
尽pipe到目前为止所给出的答案都与实际问题相符,但它们与标题不符。 所以这里是一个深受Hacker's Delight启发的解决scheme。
unsigned divu10(unsigned n) { unsigned q, r; q = (n >> 1) + (n >> 2); q = q + (q >> 4); q = q + (q >> 8); q = q + (q >> 16); q = q >> 3; r = n - (((q << 2) + q) << 1); return q + (r > 9); }
我认为这是缺乏多重指令的架构的最佳解决scheme。
当然,如果你能承受一些精确的损失,你也可以。 如果你知道你的input值的值范围,你可以拿出一个位移和一个精确的乘法。 一些例子,你可以如何划分10,60,…这样的博客描述在这个博客格式时间最快的方式可能。
temp = (ms * 205) >> 11; // 205/2048 is nearly the same as /10
你的,阿洛伊斯·克劳斯
好的划分是减法,所以是的。 向右移1(除以2)。 现在从结果中减去5,计算你减法的次数,直到小于5.结果是你做的减法的次数。 哦,划分可能会更快。
如果分频器中的逻辑电路还没有为你做这个工作,那么一个使用正常分频的5分频的混合策略可能会提高你的性能。
在一次只能移动一个地方的体系结构上,一系列明显的比较,即减去2乘以10的权力,可能比黑客的解决scheme更好。 假设一个16位的分红:
uint16_t div10(uint16_t dividend) { uint16_t quotient = 0; #define div10_step(n) \ do { if (dividend >= (n*10)) { quotient += n; dividend -= n*10; } } while (0) div10_step(0x1000); div10_step(0x0800); div10_step(0x0400); div10_step(0x0200); div10_step(0x0100); div10_step(0x0080); div10_step(0x0040); div10_step(0x0020); div10_step(0x0010); div10_step(0x0008); div10_step(0x0004); div10_step(0x0002); div10_step(0x0001); #undef div10_step if (dividend >= 5) ++quotient; // round the result (optional) return quotient; }
考虑到库巴·奥伯的回应,还有另外一个同样的观点。 它使用迭代逼近的结果,但我不希望有任何令人惊讶的performance。
假设我们必须findx
,其中x = v / 10
。
我们将使用逆运算v = x * 10
因为它具有当x = a + b
,那么x * 10 = a * 10 + b * 10
。
用x
作为variables保持迄今为止的最佳逼近结果。 当search结束时, x
将保留结果。 我们将x
每个位b
从最高有效位设置到较低有效位,逐个比较(x + b) * 10
与v
。 如果它小于或等于v
,则位b
被设置在x
。 为了testing下一个位,我们简单地把b移到右边(除以2)。
我们可以通过在其他variables中保持x * 10
和b * 10
来避免乘以10。
这产生以下algorithm将v
除以10。
uin16_t x = 0, x10 = 0, b = 0x1000, b10 = 0xA000; while (b != 0) { uint16_t t = x10 + b10; if (t <= v) { x10 = t; x |= b; } b10 >>= 1; b >>= 1; } // x = v / 10
编辑:为了得到Kuba Ober的algorithm,避免了variablesx10
的需要,我们可以从v
和v10
减去b10
。 在这种情况下, x10
不再需要了。 algorithm变成了
uin16_t x = 0, b = 0x1000, b10 = 0xA000; while (b != 0) { if (b10 <= v) { v -= b10; x |= b; } b10 >>= 1; b >>= 1; } // x = v / 10
循环可以展开,并且b
和b10
的不同值可以被预先计算为常数。