Divida el número de punto flotante en partes fraccionarias e integrales

Estoy escribiendo una clase de plantilla diseñada para trabajar con cualquier tipo de punto flotante. Para algunos de los métodos, necesito dividir un número en sus partes entera y fraccionaria. Con los tipos primitivos de coma flotante, puedo convertirlos en un número entero para truncar la parte fraccionaria, pero esto no funcionaría con una clase de números grandes. Idealmente, mi clase solo usaría las cuatro operaciones aritméticas básicas (suma, resta, multiplicación, división) en sus cálculos.

El siguiente método es la solución que se me ocurrió. Todo lo que hace es restar potencias de diez hasta que el número original sea menor que 1. Funciona bien, pero parece un enfoque de fuerza bruta. ¿Hay una manera más eficiente de hacer esto?

template< typename T >
class Math
{
    public:

    static T modf( T const & x, T & intpart )
    {
        T sub = 1;
        T ret = x;

        while( x >= sub )
        {
            sub *= 10;
        }

        sub /= 10;

        while( sub >= 1 )
        {
            while( ret >= sub )
            {
                ret -= sub;
            }

            sub /= 10;

        }//while [sub] > 0

        intpart = x - ret;

        return ret;
    }
}

Tenga en cuenta que eliminé el código de administración de letreros por razones de brevedad.

preguntado el 04 de julio de 12 a las 10:07

3 Respuestas

Quizás podría reemplazar el ciclo de resta con una búsqueda binaria, aunque eso no es una mejora en la clase de complejidad.

Lo que tienes requiere un número de restas aproximadamente igual a la suma de los dígitos decimales de x, mientras que una búsqueda binaria requiere un número de operaciones de suma y división por dos aproximadamente igual a 3 veces y un bit el número de dígitos decimales de x.

Con lo que está haciendo y con la búsqueda binaria, no hay una razón particular para usar potencias de 10 al buscar el límite superior, puede usar cualquier número. Algún otro número podría ser un poco más rápido en promedio, aunque probablemente dependa del tipo T.

Por cierto, yo también estaría tentado de hacer modf una plantilla de función dentro Math (o una función de plantilla gratuita en un espacio de nombres), en lugar de Math una plantilla de clase. De esa manera, puede especializarse o sobrecargar una función a la vez para tipos particulares (especialmente los tipos incorporados) sin tener que especializar la totalidad de Math.

Ejemplo:

namespace Math
{
    template <typename T>
    T modf( T const & x, T & intpart )
    { ... }
}

Llámalo así:

float f = 1.5, fint;
std::cout << Math::modf(f, fint) << '\n';

double d = 2.5, dint;
std::cout << Math::modf(d, dint) << '\n';

mpf_class ff(3.5), ffint(0);  // GNU multi-precision
std::cout << Math::modf(ff, ffint) << '\n';

Sobrecargarlo así:

namespace Math {
    double modf(double x, double &intpart) {
        return std::modf(x, &intpart);
    }

    mpf_class modf(const mpf_class &x, mpf_class &intpart) {
        intpart = floor(x);
        return x - intpart;
    }
}

Respondido 04 Jul 12, 11:07

esto suena como una buena idea, ¿puedes dar un ejemplo? Había considerado hacer los propios métodos como plantillas, pero quería mantener la interfaz lo más amigable posible para las personas que llaman. La sintaxis de las plantillas puede ser una pesadilla a veces :P - VenderSpam

@PeddleSpam: la interfaz con una plantilla de función debería terminar siendo más amigable para las personas que llaman que lo que tiene, porque las plantillas de función pueden deducir sus argumentos de plantilla a partir de los argumentos proporcionados a la llamada, mientras que las plantillas de clase no tienen esa facilidad. Se agregaron ejemplos. - steve jesop

Me gusta más la interfaz, pero para un tipo que aún no tenía una sobrecarga, el usuario aún tendría que llamar Math::modf< mpf_class >( ff, ffint ). - VenderSpam

@PeddleSpam: eso no es cierto. Si no hay sobrecarga para el tipo T, entonces T t1, t2; Math::modf(t1, t2); llamará automáticamente Math::modf<T>. Intentalo. - steve jesop

solo por curiosidad hice una nueva función llamada cmodf que extrae el componente entero de x realizando un elenco intpart = unsigned( abs( x ) ). Luego hice una ejecución cronometrada de ambas funciones usando los mismos datos de entrada y los tiempos son casi idénticos, con una diferencia promedio de 0.25%. ¿Es un elenco realmente tan ineficiente? - VenderSpam

mb use std::modf es mejor? para el tipo personalizado, puede liberar la especialización de clase de Matemáticas.

#include <cmath>
#include <iostream>

template<typename T>
class Math
{
public:
    static T modf(const T& x, T& integral_part)
    {
        return std::modf(x, &integral_part);
    }
};

int main()
{
    double d_part = 0.;
    double res = Math<double>::modf(5.2123, d_part);
    std::cout << d_part << " " << res << std::endl;
}

Respondido 04 Jul 12, 10:07

std::modf solo está sobrecargado para los tipos primitivos de punto flotante (float, double, long double) - VenderSpam

Las cosas de impulso solo funcionan si el tipo T tiene una conversión a un tipo integral, o si escribe algunas especializaciones o sobrecargas para "configurar" su tipo para boost.math. En la práctica, configurar su tipo es probablemente lo correcto, pero parece estar fuera del alcance de esta pregunta. "Idealmente, mi clase solo usaría las cuatro operaciones aritméticas básicas": lo que parece querer el interrogador es una alternativa que funcione sin necesidad de un trabajo separado para cada tipo. - steve jesop

eso es más o menos lo que quería. Hacer que el usuario configure su tipo para trabajar con mi clase parece incómodo. ¿Sería mejor especificar una interfaz que requiera la clase y especializarse en tipos primitivos? - VenderSpam

@PeddleSpam: es incómodo para quien configura el tipo, pero probablemente resultará en operaciones mucho más eficientes. La mayoría de las clases definidas por el usuario para manejar números pueden devolver sus propias partes enteras y fraccionarias rápidamente, porque "saben" dónde está el punto decimal en su representación interna del valor. La mejor manera es una interfaz de funciones gratuitas que pueda definir alguien que no sea el autor de la clase. Luego, si la persona A quiere usar sus plantillas matemáticas con un tipo B cuyo autor nunca ha oído hablar de su interfaz, A puede hacer la configuración. - steve jesop

No sé qué tan estricta es su restricción de "idealmente usar solo operaciones matemáticas", pero no obstante, para la parte fraccionaria, ¿podría extraerla en una cadena y convertirla de nuevo en un flotador?

Respondido 04 Jul 12, 10:07

convertir los números en cadenas fue una de mis primeras ideas, pero probablemente sería incluso menos eficiente que lo que tengo ahora. - VenderSpam

No sé la respuesta a cuán eficiente sería usar cadenas, pero ¿resolvería este enfoque los problemas de escritura que tiene actualmente, creando una plantilla más simple y genérica? - Rey de ed

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.