Saltar al contenido

¿Cómo comparar correctamente un valor entero y un valor de punto flotante?

No dudes en divulgar nuestro sitio y códigos en tus redes, ayúdanos a hacer crecer esta comunidad.

Solución:

(Restringir esta respuesta a números positivos; la generalización es trivial).

  1. Obtenga el número de bits en su exponente para el float en su plataforma junto con el radix. Si tiene un IEEE754 de 32 bits float entonces este es un paso trivial.

  2. Utilice (1) para calcular el valor no entero más grande que se puede almacenar en su float. std::numeric_limits no especifica este valor, molesto, por lo que debe hacerlo usted mismo. Para IEEE754 de 32 bits, puede elegir la opción fácil: 8388607.5 es el tipo no integral más grande float.

  3. Si tu float es menor o igual que (2), luego verifique si es un número entero o no. Si no es un número entero, puede redondearlo adecuadamente para no invalidar el <.

  4. En este punto, el float es un entero. Compruebe si está dentro del rango de su long long. Si está fuera de rango, entonces el resultado de < es conocida.

  5. Si llegas tan lejos, puedes lanzar tu float a un long longy haga la comparación.

Esto es lo que terminé con.

El crédito por el algoritmo es para @chux; su enfoque parece superar las otras sugerencias. Puede encontrar algunas implementaciones alternativas en el historial de edición.

Si puede pensar en alguna mejora, las sugerencias son bienvenidas.

#include 
#include 
#include 

enum partial_ordering less, equal, greater, unordered;

template 
partial_ordering compare_int_float(I i, F f)

    if constexpr (std::is_integral_v && std::is_floating_point_v)
    
        return compare_int_float(f, i);
    
    else
     I_max_as_F_plus_1 * 2 == I_max_as_F_plus_1;
        if constexpr (limits_overflow)
        
            // Manually check for special floating-point values.
            if (std::isinf(f))
                return f > 0 ? less : greater;
            if (std::isnan(f))
                return unordered;
        

        if (limits_overflow 

Si desea experimentar con él, aquí hay algunos casos de prueba:

#include 
#include 
#include  

void compare_print(long long a, float b, int n = 0)

    if (n == 0)
    
        auto result = compare_int_float(a,b);
        std::cout << a << ' ' << "<=>?"[int(result)] << ' ' << b << 'n';
    
    else
    
        for (int i = 0; i < n; i++)
            b = std::nextafter(b, -INFINITY);

        for (int i = 0; i <= n*2; i++)
        
            compare_print(a, b);
            b = std::nextafter(b, INFINITY);
        

        std::cout << 'n';
    


int main()
    
    std::cout << std::setprecision(1000);

    compare_print(999999984306749440,
                  999999984306749440.f, 2);

    compare_print(999999984306749439,
                  999999984306749440.f, 2);

    compare_print(100,
                  100.f, 2);

    compare_print(-100,
                  -100.f, 2);

    compare_print(0,
                  0.f, 2);

    compare_print((long long)0x8000'0000'0000'0000,
                  (long long)0x8000'0000'0000'0000, 2);

    compare_print(42, INFINITY);
    compare_print(42, -INFINITY);
    compare_print(42, NAN);
    std::cout << 'n';

    compare_print(1388608,
                  1388608.f, 2);

    compare_print(12388608,
                  12388608.f, 2);

(ejecuta el código)

Para comparar un FP f y enteroi por la igualdad:

(El código es representativo y utiliza la comparación de float y long long como ejemplo)

  1. Si f es un NaN, infinito o tiene una parte fraccionaria (tal vez use frexp()), f no es igual a i.

    float ipart;
    // C++
    if (frexp(f, &ipart) != 0) return not_equal;
    // C
    if (frexpf(f, &ipart) != 0) return not_equal;
    
  2. Convierta los límites numéricos de i dentro exactamente representable Valores de FP (potencias de 2) cerca de esos límites.** Fácil de hacer si asumimos que FP no es una codificación de base 10 rara y un rango de double excede el rango en el i. Aproveche que las magnitudes de los límites enteros están cerca del número de Mersenne. (Lo sentimos, el código de ejemplo es C-ish)

    #define FP_INT_MAX_PLUS1 ((LLONG_MAX/2 + 1)*2.0)
    #define FP_INT_MIN (LLONG_MIN*1.0)
    
  3. Comparar f a sus límites

    if (f >= FP_INT_MAX_PLUS1) return not_equal;
    if (f < FP_INT_MIN) return not_equal;
    
  4. Convertir f para enteros y comparar

    return (long long) f == i;
    

Para comparar un FP f y enteroi por <, >, == o no comparable:

(Usando los límites anteriores)

  1. Prueba f >= lower limit

    if (f >= FP_INT_MIN) 
    
  2. Prueba f <= upper limit

      // reform below to cope with effects of rounding
      // if (f <= FP_INT_MAX_PLUS1 - 1)
      if (f - FP_INT_MAX_PLUS1 <= -1.0) 
    
  3. Convertir f a entero / fracción y comparar

        // at this point `f` is in the range of `i`
        long long ipart = (long long) f;
        if (ipart < i) return f_less_than_i;
        if (ipart > i) return f_more_than_i;
    
        float frac = f - ipart;
        if (frac < 0) return f_less_than_i;
        if (frac > 0) return f_more_than_i;
        return equal;
      
    
  4. Manejar casos de borde

      else return f_more_than_i;
    
    if (f < 0.0) return f_less_than_i;
    return not_comparable;
    

Simplificaciones posibles, pero quería transmitir el algoritmo.


** Se necesita código condicional adicional para hacer frente a la codificación de enteros complementarios a 2. Es bastante similar al MAX código.

Sección de Reseñas y Valoraciones

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *