Bueno, esto es un error en un código muy sencillo, del que me acabo de dar cuenta. Resulta que quiero multiplicar un número en coma flotante por 10 y asignarselo a un entero. Algo en teoría trivial, pero que requiere dos lineas de código para que salga el resultado que yo quiero:
int entero;
float flotante,angulo=-179.9;
entero=angulo*10; //Resultado de esta operación entero=-1798
flotante=angulo*10;
entero=flotante; //Resultado de esta operación entero=-1799
La línea de arriba es la que yo ponía antes de detectar el error, y como veis, se pierde una décima en la operación. Las líneas de abajo funcionan bien.
Explicación:
entero=angulo*10;
fld dword ptr [angulo] // ST0 = -1.79899993896484375e+0002
fmul dword ptr [__real@41200000] // numero 10.0
call __ftol
mov dword ptr [entero],eax
flotante=angulo*10;
fld dword ptr [angulo] // ST0 = -1.79899993896484375e+0002
fmul dword ptr [__real@41200000] // numero 10.0
fstp dword ptr [flotante] // flotante=-1799.0
entero=flotante;
fld dword ptr [flotante] // ST0 = -1.79900000000000000e+0003
call __ftol
mov dword ptr [entero],eax
Como veis, el error se produce porque la instrucción FLD carga el número -1.79899993896484375e+0002 en el coprocesador, en vez de -1799.0 y luego al pasarlo a entero con la función FISTP (que hay dentro de __ftol) se recortan los decimales y se jode una decima del número. Por defecto el copro siempre "recorta" en vez de "redondear".
En las instrucciones de abajo el error se corrige porque la instrucción FSTP redondea el número a -1799. INEXPLICABLEMENTE :o en la siguiente instrucción FLD se carga ST0 = -1.79900000000000000e+0003
Alguien me puede decir por qué en la primera operación FLD se carga ST0 = -1.79899993896484375e+0002 y en la segunda ST0 = -1.79900000000000000e+0003 ????
Tambien me gustaría saber si este código funciona igual en un K7, yo tengo un PIII. Solo teneis que copiar las 5 líneas en C para probarlo...
gracias y perdón por el tocho
un saludo
Acabo de probar una cosa, los números terminados en .0 ó .5 se cargan bien en el coprocesador, los demás se cargan con decimales de basura de más o de menos....
Estoy pasando algo por alto? es mi micro o todos los ordenadores del mundo mundial pierden una décima en cada multiplicación??... no entiendo nada....
Utiliza double en lugar de float, a mí me soluciona los problemas de "basura".
Todos los ordenadores del mundo pierden precisión en los últimos decimales debido a la conversión a binario del número con coma flotante. Para comprobarlo sólo tienes que cambiar de base tu -179,9 a binario y verás cómo te da un número con decimales infinitos (sabes pasar números decimales a binario ¿verdad? ;))
Sync
Pos la verdad es que yo no me acuerdo. Este... no acostumbro a hacerlo :P
Sólo recuerdo que era una coñazo impresionante y que entraba en los exámenes ^_^
La verdad es que nunca me habia dado por estudiarme la codificación de un número flotante, por vagancia (ya que yo no he estudiado informatica en la uni). Acabo de echarle un vistacillo por encima a como se hace y ahora lo comprendo. A primera vista, lo primero que se me ocurre es "vaya mierda de sistema de codificación". No sé por qué lo han hecho así, ya lo estudiaré más a fondo a ver si entiendo algo...
Yo pensaba que se codificaba de una forma más simple, en binario de toda la vida. Es decir, para el caso de un float de 32, un bit para el signo, 8 bits para el exponente y los 23 bits restantes para la mantisa. De esa forma, se tienen hasta 7 digitos decimales significativos del número. Y claro codificar el número 1799 con 23 bits es una mingada, pero tal y como se hace con la norma IEEE esta, no lo tengo tan claro.
De todas formas los decimales se pierden al cargar el número en el coprocesador. Y lo acabo de probar con DOUBLES y tambien se pierde, en este caso en la centésimas (32 bits más de precisión para salvar las décimas :-? )
Ejemplo: Si quiero almacenar un número double dentro de un entero, pero multiplicado por 100 para coger hasta las centésimas, necesito hacerlo en dos pasos y con una variable auxiliar para que me salga bien:
int entero;
double flotante,angulo=-179.91;
entero=angulo*100; //Resultado de esta operación entero=-17990
flotante=angulo*100;
entero=flotante; //Resultado de esta operación entero=-17991
saludos
No se si te has dado cuenta pero el error q tienes es pequeño por q una unidad cuando estas trabajando con 2000 no es tanto.
De todas maneras:
int abajo, redondea;
float a=0.6;
abajo=(int)a;
redondea= (int)(a+0.5);
saludos