【问题标题】:Efficient algorithm for mapping a fraction to an integer将分数映射到整数的高效算法
【发布时间】:2012-05-16 20:37:01
【问题描述】:

我有一个floatf,在 1 到 0 的范围内,我想映射到一个 intifi 相关:

f = 1/(2^i)

所以

i = log2(1/f)

我使用以下公式计算i

int i = log2f(floorf(1/f)); 

这个表达式涉及 3 个浮点运算,所以我认为它的效率相对较低。

我的问题:

  1. 一般来说,这是低效的吗? (我明白,由于平台相关的优化,这很难回答)
  2. 是否可以创建更高效​​的算法?看到这涉及到2^n,我想可以使用ints 和位移来创建更有效的算法。

【问题讨论】:

  • @Vlad 不错!固定。
  • 为什么要铺两次地板?为什么不 i = log2f(1/f) ?
  • 当“相对而言”时,它是相对于什么?与整数减法相比,没有 FPU 的嵌入式系统上的浮点日志功能非常慢,但在现代桌面上,它会比打开文件快得多。
  • @StephenNutt 好点。我已将短语更改为“一般而言”。
  • 可能会进行重大的性能优化,但它们都需要缩小问题范围。如果这适用于单个 CPU 架构,您需要什么精度,i 和 f 是否有范围限制?至于问题 1,不知道这个操作执行的频率有多高,所以无法回答。

标签: c performance algorithm


【解决方案1】:

假设f 是正数,log2(1/f) 等价于-log2(f),这应该可以让您简化一点:

int i=floorf(-log2f(f));

用否定代替除法可能有助于加快速度。

如果您不介意一些完全不可移植的代码,您应该能够直接提取浮点数的指数部分。不过,log2f 的良好实现可能已经做到了这一点,因此您可能会放弃可移植性而几乎没有回报。

【讨论】:

    【解决方案2】:

    您应该了解浮点数是如何存储的。在使用 4 个字节存储浮点数的典型机器中,最后 1 个字节用于存储二进制指数。如果你可以访问那部分内存,那么你就差不多完成了。

    例如。在 C 中,您可以声明一个联合结构来存储 1 个浮点数或 4 个短无符号整数(1 个字节/短整数)。您所要做的就是分配浮点数并提取存储指数的短整数)。

    此答案中提到的实际值在您的机器上可能不正确,但如果您知道正确的数字,则可以使用此方法。

    【讨论】:

    • short int 总是至少 2 个字节 - 也许你的意思是 int8_t
    • @BlueRaja-DannyPflughoeft 在 1999 年,当我在我的 pentium II 机器上使用 C 时,short int 是 1 字节 :D 正如我所提到的,我不确定数字,但使用 union 是一种方式去这里。
    • short总是被要求至少是两个字节(至少,早在 C89 中)。要么你的编译器非常糟糕,要么你的记忆不正确。
    • @BlueRaja-DannyPflughoeft 或者,可能是无符号字符。
    【解决方案3】:

    让我们看看我是否明白这一点。对于 f=0.5,1/f = 2,所以您希望 i 为 1。对于任何大于 0.5 的 f,1/f 将小于 2,因此 i 将为 0,对吗?

    For 0.5<f<=1     i=0
    For 0.25<f<0.5   i=1
    For 0.125<f<0.25 i=2
    and so on.
    

    所以i基本上是尾数前1位的从零开始的索引(考虑到指数,应该添加它)

    【讨论】:

      【解决方案4】:

      好吧,让我们假设您的浮点数遵循 IEEE754 标准,并且这些值是正确计算的。 (可以将您的值正确地表示为 float,因为 f 始终是 2 的幂。)

      查看 IEEE754 标准,您的号码 f 将始终*尾数为 1.0,因此您真正需要的是提取指数。这可以通过使用float 的二进制表示来完成:数字本身是 32 位的,指数 us 位于 24-31 位(从右到左计数)。您需要从该值中减去 127。

      请参阅online converter 和任何有关 IEEE754 标准的文档以了解更多详细信息。


      *嗯,除了非规范化的情况。非规范化浮点数的存储方式不像1*2^-2,而是像0.5*2^-1。为了处理非规范化的浮点数,我建议通过添加 0.0 将它们转换为规范化的浮点数。在您的情况下,您可以通过尾数不是 1 来轻松检测非规范化浮点数。

      【讨论】:

        【解决方案5】:

        C 有一个用于此目的的函数:frexpf(1999 C 标准第 7.12.6.4 节)。它归一化,因此指数匹配 [1/2, 1) 中的分数,因此您需要从其指数中减去 1(例如,对于 .25f,它给出的指数为 -1,因为 .25f = .5 * 2 -1,但你想要-2):

        #include <math.h>
        #include <stdio.h>
        
        
        int main(void)
        {
            int exponent;
            for (float f = 0x1p-149f; f <= 1; f += f)
            {
                frexpf(f, &exponent);
                printf("The exponent of %a is %d.\n", f, exponent-1);
            }
        
            return 0;
        }
        

        【讨论】:

          猜你喜欢
          • 2017-09-30
          • 2013-04-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-21
          • 2018-07-08
          相关资源
          最近更新 更多