6

我的整数值范围为 32-8191,我想将其映射到大致对数刻度。如果我使用基数 2,我可以只计算前导零位并将它们映射到 8 个插槽中,但这太粗略了;我需要 32 个插槽(更多会更好,但我需要它们映射到 32 位值中的位),其对数的底数约为 1.18-1.20。任何人都有一些技巧来计算这个值,或者一个合理的近似值,非常快?

我的直觉是用条件将范围分解为 2 或 3 个子范围,并为每个子范围使用一个小的查找表,但我想知道是否有一些技巧可以用 count-leading-zeros 然后细化结果,特别是因为结果不必是精确的,而只是大致对数。

4

3 回答 3

4

为什么不使用除前导位之外的接下来的两位。您可以先将数字划分为 8 个 bin,然后将接下来的两位进一步划分为每个 bin 为 4。在这种情况下,您可以使用非常快速的简单移位操作。

编辑:如果您认为使用对数是可行的解决方案。这是一般算法:

a为对数的底,范围为(b_min, b_max) = (32,8191)。您可以使用以下公式找到基础:

log(b_max/b_min) / log(a) = 32 bin

哪个给你a~1.1892026。如果将此 a 作为对数的底,则可以将范围映射(b_min, b_max)(log_a(b_min), log_a(b_max)) = (20.0004,52.0004).

现在您只需将所有元素减去 a20.0004即可获得 range (0,32)。它保证所有元素都是对数一致的。完毕

注意:任何一个元素都可能由于数值错误而超出范围。你应该自己计算它的确切值。

注2:log_a(b) = log(b)/log(a)

于 2010-12-11T04:52:39.610 回答
2

我刚刚提出的基于 IEEE 754 浮点的答案:

((union { float v; uint32_t r; }){ x }.r >> 21 & 127) - 16

它大致以对数方式将 32-8192 映射到 0-31(与 hwlau 的答案相同)。

改进版(删去无用的按位和):

((union { float v; uint32_t r; }){ x }.r >> 21) - 528
于 2010-12-11T05:13:46.300 回答
2

表查找是一种选择,该表不是那么大。如果 8K 表太大,并且您有计数前导零指令,则可以使用前几位的表查找。

nbits = 32 - count_leading_zeros(v)  # number of bits in number
highbits = v >> (nbits - 4)          # top 4 bits.  Top bit is always a 1.
log_base_2 = nbits + table[highbits & 0x7]

您使用 log_2 的近似值填充的表

table[i] = approx(log_2(1 + i/8.0))

如果您想保留整数运算,请将最后一行乘以一个方便的因子。

于 2010-12-11T04:53:29.863 回答