有一个很酷的技巧可以将二进制中的 1 个数字和一个固定宽度的整数相加。在每次迭代中,您将每个数字的一半分成两个值,将一个值向下移位,然后相加。第一次迭代,将其他数字分开。第二次迭代,数字对,依此类推。
鉴于 27 是 00011011 作为 8 位二进制,该过程是......
00010001 + 00000101 = 00010110 <- every other digit step
00010010 + 00000001 = 00010011 <- pairs of digits
00000011 + 00000001 = 00000100 <- quads, giving final result 4
你可以用小数做一个类似的技巧,但它比简单的循环效率低,除非你有一个十进制数字的直接表示,并通过快速操作将选定的数字归零并进行数字移位。所以对于 12345678 你得到...
02040608 + 01030507 = 03071115 <- every other digit
00070015 + 00030011 = 00100026 <- pairs
00000026 + 00000010 = 00000036 <- quads, final result
所以 1+2+3+4+5+6+7+8 = 36,这是正确的,但只有当你的数字表示是固定宽度的十进制时,你才能有效地做到这一点。它总是需要 lg(n) 次迭代,其中 lg 表示以两个为底的对数,然后向上取整。
为了稍微扩展一下(基于评论中的讨论),让我们假装这是理智的,有点......
如果你计算个位数的加法,实际上这里的工作比简单的循环要多。与计算位的按位技巧一样,这个想法是重新排序这些加法(使用关联性),然后并行计算尽可能多的计算,使用单个全角加法来实现两个半角加法,四个四分之一宽度加法等。数字清除和数字移位操作有很大的开销,如果将其实现为循环(计算或查找每个步骤的数字掩码和移位距离值),则开销更大。“循环”可能应该完全展开,并且那些掩码和移位距离作为常量包含在代码中以避免这种情况。
支持二进制编码十进制 (BCD)的处理器可以处理此问题。数字屏蔽和数字移位将使用位屏蔽和位移来实现,因为每个十进制数字将被编码为 4(或更多)位,与其他数字的编码无关。
一个问题是,如今 BCD 支持非常少见。它曾经在 8 位和 16 位时代相当普遍,但据我所知,现在仍然支持它的处理器主要是为了向后兼容。原因包括...
非常早期的处理器不包括硬件乘法和除法。对这些操作的硬件支持意味着现在将二进制转换为十进制更加容易和高效。现在几乎所有东西都使用二进制,而 BCD 几乎被遗忘了。
库中有十进制数字表示,但很少有高级语言曾经为硬件 BCD 提供可移植支持,因此,由于汇编程序不再是大多数开发人员的现实选择,BCD 支持就不再被使用。
随着数字变大,即使打包的 BCD 打包效率也很低。以 10^x 为底的数字表示具有以 10 为底的最重要的属性,并且很容易被解码为十进制。Base 1000 只需要每三位数字 10 位,而不是 12 位,因为 2^10 是 1024。这足以表明您获得了 32 位的额外十进制数字 - 9 位而不是 8 位 - 而且您还剩下 2 位,例如符号位。
问题是,要使这种数字总计算法完全值得,您需要使用可能至少 32 位(8 位)的固定宽度十进制小数。这为(完全展开的)简单循环提供了 12 次操作(6 个掩码、3 个班次、3 个加法)而不是 15 次加法。不过,这是一个临界增益 - 代码中的其他问题很容易意味着它实际上更慢。
64 位(16 个十进制数字)的效率增益更明显,因为仍然只有 16 个操作(8 个掩码、4 个移位、4 个加法)而不是 31 个,但是找到支持 64 位 BCD 操作的处理器的可能性似乎很小。即使你这样做了,你多久需要一次?似乎不太可能值得付出努力和失去可移植性。