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).
-
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 bitsfloat
entonces este es un paso trivial. -
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 grandefloat
. -
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<
. -
En este punto, el
float
es un entero. Compruebe si está dentro del rango de sulong long
. Si está fuera de rango, entonces el resultado de<
es conocida. -
Si llegas tan lejos, puedes lanzar tu
float
a unlong long
y 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)
-
Si
f
es un NaN, infinito o tiene una parte fraccionaria (tal vez usefrexp()
),f
no es igual ai
.float ipart; // C++ if (frexp(f, &ipart) != 0) return not_equal; // C if (frexpf(f, &ipart) != 0) return not_equal;
-
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 dedouble
excede el rango en eli
. 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)
-
Comparar
f
a sus límitesif (f >= FP_INT_MAX_PLUS1) return not_equal; if (f < FP_INT_MIN) return not_equal;
-
Convertir
f
para enteros y compararreturn (long long) f == i;
Para comparar un FP f
y enteroi
por <
, >
, ==
o no comparable:
(Usando los límites anteriores)
-
Prueba
f >= lower limit
if (f >= FP_INT_MIN)
-
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)
-
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;
-
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.