c++ - Precisión de la multiplicación por 1.0 e int a la conversión de flotación
floating-point type-conversion (4)
¿Es seguro asumir que la condición (int)(i * 1.0f) == i
es verdadera para cualquier número entero i
?
No, los números de coma flotante IEEE-754 tienen un rango dinámico mayor que los enteros a costa de una precisión entera para el mismo ancho de bit.
Ver por ejemplo el resultado de este pequeño fragmento:
int main() {
int x = 43046721;
float y = x;
printf("%d/n", x);
printf("%f/n", y);
}
43046721
no se puede representar correctamente en los 24 bits de precisión disponibles en un número float
32 bits, por lo que el resultado es algo 43046721
lo siguiente:
43046721
43046720.000000
De hecho, esperaría que cualquier número impar por encima de 16.777.216 tenga el mismo problema al convertir a un número float
32 bits.
Algunos puntos de interés:
Esto tiene más que ver con la conversión implícita de flotación que con la multiplicación misma.
Esto no es de ninguna manera exclusivo de C, por ejemplo, Java también está sujeto al mismo problema.
La mayoría de los compiladores tienen opciones de optimización que pueden afectar la forma en que se manejan dichas conversiones, ignorando ciertas restricciones del estándar. En tal caso,
(int)((float)x * 1.0f) == x
siempre podría sertrue
si el compilador optimiza la conversión afloat
y viceversa.
No.
Si i
es suficientemente grande que int(float(i)) != i
(suponiendo que float es IEEE-754 de precisión simple, i = 0x1000001
suficiente para exhibir esto) entonces esto es falso, porque la multiplicación por 1.0f
fuerza a una conversión a float
, que cambia el valor aunque la multiplicación posterior no lo haga.
Sin embargo, si i
soy un entero de 32 bits y el double
es double
IEEE-754, entonces es cierto que int(i*1.0) == i
.
Para ser totalmente claro, la multiplicación por 1.0f
es exacta. Es la conversión de int
a float
que puede no ser.
No, es absolutamente incorrecto para todos los enteros debido al tipo de molde. Código de verificación.
#include <stdio.h>
int main()
{
int i = 0;
for (; i < 2147483647; ++i) {
if ((int)(i * 1.0f) != i) {
printf("not equal/n");
break;
}
}
printf("out of the loop/n");
getchar();
return 0;
}
Este código supone que toma un entero de 32 bits
No, el comportamiento es implementación definida porque C y C ++ no requieren IEEE-754, aunque esa es la representación más común de lejos.
Para estar seguro de que se usa IEEE-754:
- en C, use
#ifdef __STDC_IEC_559__
- en C ++, use las
std::numeric_limits<float>::is_iec559