POWF的性能更高(10,log10f(x))是否有更高的实现

发布于 2025-01-21 17:33:13 字数 867 浏览 1 评论 0原文

我需要将浮子截断至最接近10的功率。例如,1.1将截断至1.0和4.7e3将截断至1E3。我目前正在使用看似复杂的powf(10,floorf(log10f(x)))。我想知道是否有更好的性能(如更快的执行速度)解决方案吗?我的目标CPU架构既是X86-64和ARM64。

#include <stdio.h>
#include <math.h>

int main()
{
  float x = 1.1e5f;
  while (x > 1e-6f)
  {
    float y = powf(10,floorf(log10f(x)));
    printf("%e ==> %g\n", x, y); 
    x /= 5.0f;
  }
}

运行时,这会产生

1.100000e+05 ==> 100000
2.200000e+04 ==> 10000
4.400000e+03 ==> 1000
8.800000e+02 ==> 100
1.760000e+02 ==> 100
3.520000e+01 ==> 10
7.040000e+00 ==> 1
1.408000e+00 ==> 1
2.816000e-01 ==> 0.1
5.632000e-02 ==> 0.01
1.126400e-02 ==> 0.01
2.252800e-03 ==> 0.001
4.505600e-04 ==> 0.0001
9.011199e-05 ==> 1e-05
1.802240e-05 ==> 1e-05
3.604480e-06 ==> 1e-06

I have a need to truncate a float to the nearest power of 10. For example, 1.1 would truncate to 1.0 and 4.7e3 would truncate to 1e3. I am currently doing it with the seemingly complicated powf(10,floorf(log10f(x))). I am wondering whether there is a better performing (as in faster execution speed) solution? My target CPU architecture is both x86-64 and arm64.

#include <stdio.h>
#include <math.h>

int main()
{
  float x = 1.1e5f;
  while (x > 1e-6f)
  {
    float y = powf(10,floorf(log10f(x)));
    printf("%e ==> %g\n", x, y); 
    x /= 5.0f;
  }
}

when run, this produces

1.100000e+05 ==> 100000
2.200000e+04 ==> 10000
4.400000e+03 ==> 1000
8.800000e+02 ==> 100
1.760000e+02 ==> 100
3.520000e+01 ==> 10
7.040000e+00 ==> 1
1.408000e+00 ==> 1
2.816000e-01 ==> 0.1
5.632000e-02 ==> 0.01
1.126400e-02 ==> 0.01
2.252800e-03 ==> 0.001
4.505600e-04 ==> 0.0001
9.011199e-05 ==> 1e-05
1.802240e-05 ==> 1e-05
3.604480e-06 ==> 1e-06

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

剩余の解释 2025-01-28 17:33:13

可以使用查找表来加快计算。该技术应适用于所有正常浮点数。如果没有一些专用逻辑,则亚正态分数,而NAN将无法使用,而无穷大可以通过表格中的极端值来处理。

尽管我希望这种技术实际上要比原始实施更快,但仍需要测量。

该代码使用C ++ 20 std :: bit_castfloat值提取指数。如果没有,则存在其他较旧技术,例如frexpf

#include <bit>
#include <cstdint>
#include <cstdio>
#include <limits>

constexpr float magnitudeLUT[] = { 
    0.f,    1e-38f, 1e-38f, 1e-38f, 1e-38f, 1e-37f, 1e-37f, 1e-37f, 1e-36f, 1e-36f, 1e-36f, 1e-35f, 
    1e-35f, 1e-35f, 1e-35f, 1e-34f, 1e-34f, 1e-34f, 1e-33f, 1e-33f, 1e-33f, 1e-32f, 1e-32f, 1e-32f, 
    1e-32f, 1e-31f, 1e-31f, 1e-31f, 1e-30f, 1e-30f, 1e-30f, 1e-29f, 1e-29f, 1e-29f, 1e-28f, 1e-28f, 
    1e-28f, 1e-28f, 1e-27f, 1e-27f, 1e-27f, 1e-26f, 1e-26f, 1e-26f, 1e-25f, 1e-25f, 1e-25f, 1e-25f, 
    1e-24f, 1e-24f, 1e-24f, 1e-23f, 1e-23f, 1e-23f, 1e-22f, 1e-22f, 1e-22f, 1e-22f, 1e-21f, 1e-21f, 
    1e-21f, 1e-20f, 1e-20f, 1e-20f, 1e-19f, 1e-19f, 1e-19f, 1e-19f, 1e-18f, 1e-18f, 1e-18f, 1e-17f, 
    1e-17f, 1e-17f, 1e-16f, 1e-16f, 1e-16f, 1e-16f, 1e-15f, 1e-15f, 1e-15f, 1e-14f, 1e-14f, 1e-14f, 
    1e-13f, 1e-13f, 1e-13f, 1e-13f, 1e-12f, 1e-12f, 1e-12f, 1e-11f, 1e-11f, 1e-11f, 1e-10f, 1e-10f, 
    1e-10f, 1e-10f, 1e-09f, 1e-09f, 1e-09f, 1e-08f, 1e-08f, 1e-08f, 1e-07f, 1e-07f, 1e-07f, 1e-07f, 
    1e-06f, 1e-06f, 1e-06f, 1e-05f, 1e-05f, 1e-05f, 1e-04f, 1e-04f, 1e-04f, 1e-04f, 1e-03f, 1e-03f, 
    1e-03f, 1e-02f, 1e-02f, 1e-02f, 1e-01f, 1e-01f, 1e-01f, 1e+00f, 1e+00f, 1e+00f, 1e+00f, 1e+01f, 
    1e+01f, 1e+01f, 1e+02f, 1e+02f, 1e+02f, 1e+03f, 1e+03f, 1e+03f, 1e+03f, 1e+04f, 1e+04f, 1e+04f, 
    1e+05f, 1e+05f, 1e+05f, 1e+06f, 1e+06f, 1e+06f, 1e+06f, 1e+07f, 1e+07f, 1e+07f, 1e+08f, 1e+08f, 
    1e+08f, 1e+09f, 1e+09f, 1e+09f, 1e+09f, 1e+10f, 1e+10f, 1e+10f, 1e+11f, 1e+11f, 1e+11f, 1e+12f, 
    1e+12f, 1e+12f, 1e+12f, 1e+13f, 1e+13f, 1e+13f, 1e+14f, 1e+14f, 1e+14f, 1e+15f, 1e+15f, 1e+15f, 
    1e+15f, 1e+16f, 1e+16f, 1e+16f, 1e+17f, 1e+17f, 1e+17f, 1e+18f, 1e+18f, 1e+18f, 1e+18f, 1e+19f, 
    1e+19f, 1e+19f, 1e+20f, 1e+20f, 1e+20f, 1e+21f, 1e+21f, 1e+21f, 1e+21f, 1e+22f, 1e+22f, 1e+22f, 
    1e+23f, 1e+23f, 1e+23f, 1e+24f, 1e+24f, 1e+24f, 1e+24f, 1e+25f, 1e+25f, 1e+25f, 1e+26f, 1e+26f, 
    1e+26f, 1e+27f, 1e+27f, 1e+27f, 1e+27f, 1e+28f, 1e+28f, 1e+28f, 1e+29f, 1e+29f, 1e+29f, 1e+30f, 
    1e+30f, 1e+30f, 1e+31f, 1e+31f, 1e+31f, 1e+31f, 1e+32f, 1e+32f, 1e+32f, 1e+33f, 1e+33f, 1e+33f, 
    1e+34f, 1e+34f, 1e+34f, 1e+34f, 1e+35f, 1e+35f, 1e+35f, 1e+36f, 1e+36f, 1e+36f, 1e+37f, 1e+37f, 
    1e+37f, 1e+37f, 1e+38f, 1e+38f, std::numeric_limits<float>::infinity() };

float decimalMagnitude(float val)
{
    uint32_t intVal = std::bit_cast<uint32_t>(val);
    uint8_t exponent = intVal >> 23;

    if (val >= magnitudeLUT[exponent + 1])
        return magnitudeLUT[exponent + 1];
    else
        return magnitudeLUT[exponent];
}

int main()
{
    for (float v = 1e-38f; v < 1e38f; v *= 1.78)
        printf("%e => %e\n", v, decimalMagnitude(v));
}

It is possible to use a lookup table to speed up the computation. This technique should work for all normal floating point numbers. Subnormal numbers and NaN won't work without some dedicated logic, 0 and infinity can be handled by extreme values in the table.

Although I expect this technique to be actually faster than original implementation, measurements are needed.

The code uses C++20 std::bit_cast to extract the exponent from the float value. If not available, other older techniques like frexpf exist.

#include <bit>
#include <cstdint>
#include <cstdio>
#include <limits>

constexpr float magnitudeLUT[] = { 
    0.f,    1e-38f, 1e-38f, 1e-38f, 1e-38f, 1e-37f, 1e-37f, 1e-37f, 1e-36f, 1e-36f, 1e-36f, 1e-35f, 
    1e-35f, 1e-35f, 1e-35f, 1e-34f, 1e-34f, 1e-34f, 1e-33f, 1e-33f, 1e-33f, 1e-32f, 1e-32f, 1e-32f, 
    1e-32f, 1e-31f, 1e-31f, 1e-31f, 1e-30f, 1e-30f, 1e-30f, 1e-29f, 1e-29f, 1e-29f, 1e-28f, 1e-28f, 
    1e-28f, 1e-28f, 1e-27f, 1e-27f, 1e-27f, 1e-26f, 1e-26f, 1e-26f, 1e-25f, 1e-25f, 1e-25f, 1e-25f, 
    1e-24f, 1e-24f, 1e-24f, 1e-23f, 1e-23f, 1e-23f, 1e-22f, 1e-22f, 1e-22f, 1e-22f, 1e-21f, 1e-21f, 
    1e-21f, 1e-20f, 1e-20f, 1e-20f, 1e-19f, 1e-19f, 1e-19f, 1e-19f, 1e-18f, 1e-18f, 1e-18f, 1e-17f, 
    1e-17f, 1e-17f, 1e-16f, 1e-16f, 1e-16f, 1e-16f, 1e-15f, 1e-15f, 1e-15f, 1e-14f, 1e-14f, 1e-14f, 
    1e-13f, 1e-13f, 1e-13f, 1e-13f, 1e-12f, 1e-12f, 1e-12f, 1e-11f, 1e-11f, 1e-11f, 1e-10f, 1e-10f, 
    1e-10f, 1e-10f, 1e-09f, 1e-09f, 1e-09f, 1e-08f, 1e-08f, 1e-08f, 1e-07f, 1e-07f, 1e-07f, 1e-07f, 
    1e-06f, 1e-06f, 1e-06f, 1e-05f, 1e-05f, 1e-05f, 1e-04f, 1e-04f, 1e-04f, 1e-04f, 1e-03f, 1e-03f, 
    1e-03f, 1e-02f, 1e-02f, 1e-02f, 1e-01f, 1e-01f, 1e-01f, 1e+00f, 1e+00f, 1e+00f, 1e+00f, 1e+01f, 
    1e+01f, 1e+01f, 1e+02f, 1e+02f, 1e+02f, 1e+03f, 1e+03f, 1e+03f, 1e+03f, 1e+04f, 1e+04f, 1e+04f, 
    1e+05f, 1e+05f, 1e+05f, 1e+06f, 1e+06f, 1e+06f, 1e+06f, 1e+07f, 1e+07f, 1e+07f, 1e+08f, 1e+08f, 
    1e+08f, 1e+09f, 1e+09f, 1e+09f, 1e+09f, 1e+10f, 1e+10f, 1e+10f, 1e+11f, 1e+11f, 1e+11f, 1e+12f, 
    1e+12f, 1e+12f, 1e+12f, 1e+13f, 1e+13f, 1e+13f, 1e+14f, 1e+14f, 1e+14f, 1e+15f, 1e+15f, 1e+15f, 
    1e+15f, 1e+16f, 1e+16f, 1e+16f, 1e+17f, 1e+17f, 1e+17f, 1e+18f, 1e+18f, 1e+18f, 1e+18f, 1e+19f, 
    1e+19f, 1e+19f, 1e+20f, 1e+20f, 1e+20f, 1e+21f, 1e+21f, 1e+21f, 1e+21f, 1e+22f, 1e+22f, 1e+22f, 
    1e+23f, 1e+23f, 1e+23f, 1e+24f, 1e+24f, 1e+24f, 1e+24f, 1e+25f, 1e+25f, 1e+25f, 1e+26f, 1e+26f, 
    1e+26f, 1e+27f, 1e+27f, 1e+27f, 1e+27f, 1e+28f, 1e+28f, 1e+28f, 1e+29f, 1e+29f, 1e+29f, 1e+30f, 
    1e+30f, 1e+30f, 1e+31f, 1e+31f, 1e+31f, 1e+31f, 1e+32f, 1e+32f, 1e+32f, 1e+33f, 1e+33f, 1e+33f, 
    1e+34f, 1e+34f, 1e+34f, 1e+34f, 1e+35f, 1e+35f, 1e+35f, 1e+36f, 1e+36f, 1e+36f, 1e+37f, 1e+37f, 
    1e+37f, 1e+37f, 1e+38f, 1e+38f, std::numeric_limits<float>::infinity() };

float decimalMagnitude(float val)
{
    uint32_t intVal = std::bit_cast<uint32_t>(val);
    uint8_t exponent = intVal >> 23;

    if (val >= magnitudeLUT[exponent + 1])
        return magnitudeLUT[exponent + 1];
    else
        return magnitudeLUT[exponent];
}

int main()
{
    for (float v = 1e-38f; v < 1e38f; v *= 1.78)
        printf("%e => %e\n", v, decimalMagnitude(v));
}
飘过的浮云 2025-01-28 17:33:13

我会说不要流汗。除非该程序花费大量时间进行这种截断,否则不值得优化可能超级快速的内容。但是,如果您想针对常见情况(1E-2&lt; = x&lt; = 10)进行优化,那么您可以尝试使用32位整数算术与1E-2、1e-1、1的二进制表示形式进行比较,和10(例如,1E-1为0x3DCCCCCD);如果超出该范围,则可以落在浮点版本上。只有实验才能确定这是否实际运行速度是否更快。

I would say don't sweat it. Unless the program is spending a large proportion of its time doing this truncation, it's not worth optimising what is probably super-fast anyway. But if you wanted to optimise for your common cases (1e-2 <= x <= 10), then you might try using 32-bit integer arithmetic to compare with the binary representations of 1e-2, 1e-1, 1, and 10 (for instance, 1e-1 is 0x3dcccccd) ; if it's outside that range, you can fall back on the floating point version. Only experimentation will determine if this actually runs faster.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文