Necesito truncar un flotante a la potencia más cercana de 10. Por ejemplo, 1.1 se truncaría a 1.0 y 4.7e3 se truncaría a 1e3. Actualmente lo estoy haciendo con el aparentemente complicado powf(10,floorf(log10f(x))) . Me pregunto si existe una solución de mejor rendimiento (como en una velocidad de ejecución más rápida). Mi arquitectura de CPU objetivo es tanto x86-64 como 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; } }cuando se ejecuta, esto produce
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-06Es posible utilizar una tabla de búsqueda para acelerar el cálculo. Esta técnica debería funcionar para todos los números de coma flotante normales . Los números subnormales y NaN no funcionarán sin una lógica dedicada, 0 e infinito pueden manejarse con valores extremos en la tabla.
Aunque espero que esta técnica sea más rápida que la implementación original, se necesitan mediciones.
El código usa C++20 std::bit_cast para extraer el exponente del valor float . Si no está disponible, existen otras técnicas más antiguas como 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)); }Yo diría que no te preocupes. A menos que el programa dedique una gran parte de su tiempo a hacer este truncamiento, no vale la pena optimizar lo que probablemente sea súper rápido de todos modos. Pero si quisiera optimizar para sus casos comunes (1e-2 <= x <= 10), entonces podría intentar usar la aritmética de enteros de 32 bits para comparar con las representaciones binarias de 1e-2, 1e-1, 1 y 10 (por ejemplo, 1e-1 es 0x3dcccccd); si está fuera de ese rango, puede recurrir a la versión de coma flotante. Solo la experimentación determinará si esto realmente funciona más rápido.