是否可以通过使用纯位移,加法,减法和可能乘法将无符号整数除以10?使用资源非常有限且分工缓慢的处理器。
答案 0 :(得分:55)
以下是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
即使在具有慢速乘法指令的机器上,这也会比软件划分更快。
答案 1 :(得分:30)
虽然到目前为止给出的答案与实际问题相符,但它们与标题不符。所以这是一个受Hacker's Delight启发的解决方案,它真正只使用了位移。
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);
}
我认为这是缺乏多重指令的架构的最佳解决方案。
答案 2 :(得分:15)
当然,如果你可以忍受一些精确的损失,你可以。如果你知道输入值的值范围,你可以得到一个位移和一个精确的乘法。 一些例子如何划分10,60 ......就像本博客中描述的格式time the fastest way一样。
temp = (ms * 205) >> 11; // 205/2048 is nearly the same as /10
答案 3 :(得分:3)
考虑到库巴奥伯的回应,还有另外一个回应。 它使用结果的迭代近似,但我不希望任何令人惊讶的表现。
我们必须找到x
x = v / 10
。
我们将使用逆操作v = x * 10
,因为它具有良好的属性,即x = a + b
,然后是x * 10 = a * 10 + b * 10
。
让x
作为变量保持到目前为止最好的结果近似值。搜索结束时,x
将保留结果。我们会将b
的{{1}}的每个位x
从最重要的位置设置为不太重要的位,逐个将(x + b) * 10
与v
进行比较。如果它小于或等于v
,则在b
中设置位x
。为了测试下一位,我们只需将b一个位置向右移动(除以2)。
我们可以通过在其他变量中保留x * 10
和b * 10
来避免乘以10。
这会产生以下算法,将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的算法,避免了变量x10
的需要,我们可以从b10
和{{1}中减去v
而是。在这种情况下,不再需要v10
。算法变为
x10
循环可以展开,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
的不同值可以预先计算为常量。
答案 4 :(得分:2)
分裂是减法,所以是的。向右移1(除以2)。现在从结果中减去5,计算减法的次数,直到值小于5.结果是你做的减法次数。哦,划分可能会更快。
如果分频器中的逻辑尚未为您执行此操作,则使用正常除法将右移然后除以5的混合策略可能会提高性能。
答案 5 :(得分:2)
在一次只能移动一个地方的架构上,一系列显着的比较减少2的幂乘以10可能比黑客的喜悦解决方案更好。假设有16位红利:
if (isset($_SERVER['CONTENT_TYPE'])
and stripos($_SERVER['CONTENT_TYPE'], 'application/json') !== false
) {
$jsonEncoded = file_get_contents('php://input');
$jsonDecoded = json_decode($jsonEncoded, true);
if (is_array($jsonDecoded)) {
foreach ($jsonDecoded as $varName => $varValue) {
$_POST[$varName] = $varValue;
}
}
}
答案 6 :(得分:1)
要稍微扩展Alois的答案,我们可以将建议的y = (x * 205) >> 11
扩展一些倍数/班次:
y = (ms * 1) >> 3 // first error 8
y = (ms * 2) >> 4 // 8
y = (ms * 4) >> 5 // 8
y = (ms * 7) >> 6 // 19
y = (ms * 13) >> 7 // 69
y = (ms * 26) >> 8 // 69
y = (ms * 52) >> 9 // 69
y = (ms * 103) >> 10 // 179
y = (ms * 205) >> 11 // 1029
y = (ms * 410) >> 12 // 1029
y = (ms * 820) >> 13 // 1029
y = (ms * 1639) >> 14 // 2739
y = (ms * 3277) >> 15 // 16389
y = (ms * 6554) >> 16 // 16389
y = (ms * 13108) >> 17 // 16389
y = (ms * 26215) >> 18 // 43699
y = (ms * 52429) >> 19 // 262149
y = (ms * 104858) >> 20 // 262149
y = (ms * 209716) >> 21 // 262149
y = (ms * 419431) >> 22 // 699059
y = (ms * 838861) >> 23 // 4194309
y = (ms * 1677722) >> 24 // 4194309
y = (ms * 3355444) >> 25 // 4194309
y = (ms * 6710887) >> 26 // 11184819
y = (ms * 13421773) >> 27 // 67108869
每行都是一个独立的计算,并且您将在注释中显示的值处看到第一个“错误” /不正确的结果。通常情况下,您最好对给定的误差值进行最小的偏移,因为这样可以最大程度地减少在计算中存储中间值所需的额外位,例如(x * 13) >> 7
比(x * 52) >> 9
“更好”,因为它只需要少一点点的开销,而两者都会在68以上时给出错误的答案。
如果您要计算更多这些内容,可以使用以下(Python)代码:
def mul_from_shift(shift):
mid = 2**shift + 5.
return int(round(mid / 10.))
我做了明显的事情来计算这种近似何时开始出现错误:
def first_err(mul, shift):
i = 1
while True:
y = (i * mul) >> shift
if y != i // 10:
return i
i += 1
(请注意,//
用于“整数”除法,即它会向零截断/四舍五入)
出现“ 3/1”模式错误的原因(即8次重复3次,后跟9次)似乎是由于基数的变化,即log2(10)
为〜3.32。如果绘制错误,则会得到以下结果:
相对误差由以下公式给出:mul_from_shift(shift) / (1<<shift) - 0.1
答案 7 :(得分:0)
我在 AVR 汇编中设计了一种新方法,只有 lsr/ror 和 sub/sbc。它除以 8,然后减去除以 64 和 128 的数字,然后减去第 1,024 和第 2,048,依此类推。工作非常可靠(包括精确舍入)和快速(1 MHz 时为 370 微秒)。 16位数字的源代码在这里: http://www.avr-asm-tutorial.net/avr_en/beginner/DIV10/div10_16rd.asm 注释此源代码的页面在这里: http://www.avr-asm-tutorial.net/avr_en/beginner/DIV10/DIV10.html 我希望它有所帮助,即使这个问题已有十年历史了。 brgs, gsc
答案 8 :(得分:-1)
elemakil的注释的代码可在这里找到:https://doc.lagout.org/security/Hackers%20Delight.pdf 第233页。“无符号除以10 [和11]”